背景:TypeScript 扩展了常规的类型检查,允许开发人员创建自己的用户定义功能——类型保护——来检查对象是否符合特定类型。这对于处理联合类型、动态结构和可能具有不同类型值的 API 是必要的。
问题通常在于,通过 typeof 和 instanceof 进行的常规定义类型检查仅限于原始类型和类,而对于结构或复杂类型则无法进行定义。必须能够明确地向编译器提示在何种情况下值安全地缩小到所需类型。
解决方案是编写类型保护函数,例如 function isCat(obj: Animal): obj is Cat {...},关键点在于函数返回类型中的类型谓词。
代码示例:
interface Dog { bark: () => void; } interface Cat { meow: () => void; } type Pet = Dog | Cat; function isDog(pet: Pet): pet is Dog { return (pet as Dog).bark !== undefined; } function makeSound(pet: Pet) { if (isDog(pet)) { pet.bark(); } else { pet.meow(); } }
关键特性:
在类型保护函数中返回 true/false 是否足以让编译器缩小类型?
不。重要的是明确指定返回类型为类型谓词(例如,pet is Dog),否则即使函数只返回 true 或 false,TypeScript 也不会自动缩小值的类型。
可以在回调中使用类型保护(例如在 filter 中),那么缩小是否能够正常工作?
可以,如果类型保护正确注释,则编译器将在 filter 之后以及 forEach/callback 函数内部缩小数组元素的类型。但是,如果没有注释或写错,结果将具有联合类型,而不是具体类型。
const pets: Pet[] = [...]; const dogs = pets.filter(isDog); // TypeScript 知道,dogs: Dog[]
用户定义的类型保护与通过 typeof、instanceof 进行的常规类型检查有什么区别?
类型保护函数可以实现任何结构的检查,描述任何复杂性级别的检查,操作接口而非仅限于基本类型和类。
函数在没有类型谓词的情况下过滤用户:
function isValidAdmin(user: any): boolean { return user.isAdmin === true; } const admins = users.filter(isValidAdmin); // admins: any[]
优点:
缺点:
函数使用正确的类型谓词进行过滤:
interface Admin { name: string; isAdmin: true; } function isAdmin(user: any): user is Admin { return user && user.isAdmin === true; } const admins = users.filter(isAdmin); // admins: Admin[]
优点:
缺点: