类型保护—是一种机制,允许在代码块中基于某些检查(例如,通过 typeof、instanceof 或返回表达式 param is SomeType 的特殊函数)来细化变量的类型。
主要优点—是在编译时通过类型检查提高安全性,减少运行时错误。
示例:
interface Fish { swim: () => void } interface Bird { fly: () => void } function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } function move(pet: Fish | Bird) { if (isFish(pet)) { pet.swim(); } else { pet.fly(); } }
在这里,函数 isFish 是一个自定义类型保护。
细节:
问题: "TypeScript 编译器是否会始终只依赖于保护函数的返回值,或还会在函数内部进行其他分析?"
回答:
TypeScript 编译器仅依赖于返回值的签名 param is Type。函数保护内部发生的事情不进行正确性分析。
示例(危险错误!):
function isString(x: any): x is string { return true; } // 编译器会认为始终是字符串,尽管事实并非如此: if (isString(123)) { // 这里 x 类型为字符串,但实际上是数字 }
故事
在一个前后端共享 DTO 的项目中,忘记在自定义类型保护内部添加严格检查。结果部分数据错误地被视为所需类型,导致客户端在尝试使用缺失属性时出现故障。
故事
开发者编写了类型保护,依赖于可选字段,但是数据结构允许根本不存在该字段。最终类型的 switch-case 缺少了分支,编译器没有发出警告—运行时出现异常。
故事
在某个服务中,转换到 TypeScript 时仅依赖于内置的类型保护(typeof、instanceof)。在执行检查时如果对象原型发生变化,就会变得不正确,导致在生产环境中出现难以调试的错误。