本来想放优雅 太优雅了.jpg,后来还是好懒啊……
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 guardif (isAdmin(goodUser)) {}
};
直接使用 any
的代码不会出现任何的报错,而使用 unknown
则是会强制要求进行类型检查才能继续。
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
是 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 类型(即数字),如:
尽管 100 不在 State 中(默认应该从 0 开始,所以这里只有 0,1,2),但是传参时检查失败。不过使用字符串就可以避免这个问题了:
这里提一个我刚找到能够很好的解决我目前一个 CRUD 操作的组合技:
interface IUser {id: number;firstname: string;lastname: string;gender: string;image: string;age: number;
}const updateUser = (userId: IUser['id'],updatedUser: Partial>
) => {};
这时候查看就能够发现,所有的选项已经变成了 optional,而 id 已经从可更新的状态中移除了。
Util Types真的还蛮有用的……不过我之前看的一些教程都没讲,晚点这块继续补一下吧。