写出更优雅和稳健的 TS 代码的几个 tips

Posted GoldenaArcher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了写出更优雅和稳健的 TS 代码的几个 tips相关的知识,希望对你有一定的参考价值。

写出更优雅和稳健的 TS 代码的几个 tips

本来想放优雅 太优雅了.jpg,后来还是好懒啊……

使用 unknown 代替 any

any 的问题在于它直接关闭了 TS 的类型检查,因此一旦使用了 any,那就代表任何事情都会发生。使用 unknown 则告诉 TS:在目前这个步骤我还不知道使用的数据类型,不过当我去使用该变量的时候,我就知道这个类型是什么了(多为类型检查)。

参考一下代码:

interface IUser 
  id: number;
  firstname: string;
  lastname: string;
  gender: string;
  image: string;
  age: number;


interface IAdminUser extends IUser 
  token: string;
  addNewUser: () => void;


const isAdmin = (object: unknown): boolean => 
  return true;
;

const fetchUser = async () => 
  const res = await fetch('http://localhost:3000/users/1');

  // bad ❌
  const badUser = await res.json();
  console.log(badUser.nonExistingProp);

  // good ✅
  const goodUser: unknown = await res.json();
  console.log(goodUser.nonExistingProp);

  // add type guard
  if (isAdmin(goodUser)) 
  
;

直接使用 any 的代码不会出现任何的报错,而使用 unknown 则是会强制要求进行类型检查才能继续。

使用 is

is 也是一种类型断言,稍微修改一下上面的代码中对 user 的检查:

const isAdmin = (object: unknown): object is IAdminUser => 
  if (object !== null && typeof object === 'object') 
    return 'token' in object;
  

  return false;
;

const isRegularUser = (object: unknown): object is IUser => 
  if (object !== null && typeof object === 'object') 
    return 'token'! in object;
  

  return false;
;

在调用时,TS 就会遵从 object is IAdminUser,在返回值为 true 的时候断言返回类型为 IAdminUser,这时候 goodUser 中就会包含所有管理员权限应该有的属性和函数。

同理,如果检查下来用户不是管理员,那么该用户也就无法调用管理员才能使用的属性和函数:

另一种需要 is 的情况是 Union Type,Union Type 的定义也是 type 乱用的重灾区。

function isFish(pet: Fish | Bird): pet is Fish 
  return (pet as Fish).swim !== undefined;


// Both calls to 'swim' and 'fly' are now okay.
let pet = getSmallPet();

if (isFish(pet)) 
  pet.swim();
 else 
  pet.fly();


const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()];
const underWater1: Fish[] = zoo.filter(isFish);
// or, equivalently
const underWater2: Fish[] = zoo.filter(isFish) as Fish[];

// The predicate may need repeating for more complex examples
const underWater3: Fish[] = zoo.filter((pet): pet is Fish => 
  if (pet.name === 'sharkey') return false;
  return isFish(pet);
);

可能对于单独一个对象而已,使用 as 进行 type casting 问题不是很大,不过对于之后要重新声明的数组等,一直使用 as 去转确实挺麻烦的。不过有了现在这个方法,就可以忽略掉 as Type[] 这种声明了。

使用 satisfies 关键词

satisfies 是 4.9 新出的一个特性,已下面代码为案例:

interface ICustomImage 
  width: number;
  height: number;
  data: string;


interface IUser 
  id: number;
  firstname: string;
  lastname: string;
  image: string | ICustomImage;


const user: IUser = 
  id: 1,
  firstname: '',
  lastname: '',
  image: 'random url',
;

可以看到,尽管声明的是一个字符串,不过因为 union type 的关系,TS 无法明确认识 user.image 究竟是字符串还是对象,但是使用 satisfies 关键词之后则是另一个情况:

TS 能够更好的通过数据类型进行断言和提示。

正确使用 enum

这里指的是尽量不要使用默认的 enum 类型(即数字),如:

尽管 100 不在 State 中(默认应该从 0 开始,所以这里只有 0,1,2),但是传参时检查失败。不过使用字符串就可以避免这个问题了:

善用 Utility Type

这里提一个我刚找到能够很好的解决我目前一个 CRUD 操作的组合技:

interface IUser 
  id: number;
  firstname: string;
  lastname: string;
  gender: string;
  image: string;
  age: number;


const updateUser = (
  userId: IUser['id'],
  updatedUser: Partial<Omit<IUser, 'id'>>
) => ;

这时候查看就能够发现,所有的选项已经变成了 optional,而 id 已经从可更新的状态中移除了。

Util Types真的还蛮有用的……不过我之前看的一些教程都没讲,晚点这块继续补一下吧。

参考

以上是关于写出更优雅和稳健的 TS 代码的几个 tips的主要内容,如果未能解决你的问题,请参考以下文章

几个你所不知道的技巧助你写出更优雅的vue.js代码

实用Tips:计算MD5值的几个方法

css中的几个小tip

js代码性能优化的几个方法

如何写出更好的Java代码

译《C# 小技巧 -- 编写更优雅的 C#》原书名《C# Tips -- Write Better C#》