描述:
CFA 几乎总是采用联合,基于代码逻辑去减少联合里面的类型数量。
大多数时候,CFA 在自然的JavaScript布尔逻辑中工作,但是有一些方法可以定义你自己的函数,这些函数会影响 TypeScript 缩小类型的方式。
简单说就是:根据代码上下文可以推断出当前变量类型。
if 语法(If Statements)
大多数窄化来自 if 语句,用不同类型操作符,在新作用域内进行窄化
typeof (用来判断原始类型)
const input = getUserInput() input // string | number if (typeof input === "string") { input // string }
instanceof (判断构造函数)
const input = getUserInput() input // string | number[] if (input instanceof Array) { input // number[] }
in (判断属性是否属于对象)
const input = getUserInput() input // string | {error: ...} if ("error" in input) { input // {error: ...} }
Array.isArray (判断是否为数组)
const input = getUserInput() input // number | number[] if (Array.isArray(input)) { input // number[] }
表达式(Expressions)
当进行布尔运算时,窄化也发生在代码的同一行
const input = getUserInput() input // string | number const inputLength = (typeof input === "string" && input.length) || input //&& input: string
识别联合(Discriminated Unions )
type JSONResponse = {status: 200, data: any} | {status: 300, to: string} | {status: 400, error: Error}
所有联合成员都有相同属性名称,CFA(Control Flow Analysis) 能识别对待
const response = getResponse() response // JSONResponse switch(response.status) { case 200: response.data case 400: redirect(response.to) case 500: response.error }
类型保护(Type Guards)类型谓词(type predicates)
定义用户定义的类型的守卫,只需要定义一个函数返回类型为类型谓词
下面例子中,isFish 就是类型守卫
type Fish = { name: string; swim: () => string }; type Bird = { name: string; fly: () => string }; const fish: Fish = { name: "sharkey", swim: () => 'asd' } const bird: Bird = { name: "noob", fly: () => 'asd' } // 未使用类型谓词 function isFish(pet: Fish | Bird) { return (pet as Fish).swim !== undefined; } const foo: Fish | Bird = Math.random() ? fish : bird; if (isFish(foo)) { // foo: Fish | Bird foo.swim() // 不能调用,不确定是 Fish 类型 } // 使用类型谓词 function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } const foo: Fish | Bird = Math.random() ? fish : bird; if (isFish(foo)) { // foo: Fish foo.swim() // ok } // 必须是当前函数签名中参数的名称(pet)。 // 如果特定类型与原始类型兼容(Fish 与 Fish | Bird),TypeScript 会将该变量缩小为该特定类型,不兼容会报错
可以使用类型守卫,过滤一个 Fish | Bird 类型数组,并获得一个 Fish 类型数组:
const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()]; const underWater1: Fish[] = zoo.filter(isFish); // 相当于 const underWater2: Fish[] = zoo.filter(isFish) as Fish[]; // 复杂的例子,谓词需要重复 const underWater3: Fish[] = zoo.filter((pet): pet is Fish => { if (pet.name === "sharkey") return false; return isFish(pet); });
断言函数(Assertion Functions)
谓词是,函数返回 true,然后根据代码逻辑在新作用域中表示特定类型
断言函数是抛出,而不是返回 false,然后改变当前作用域表示特定类型
type SuccessResponse = {data: string} type ErrorResponse = {msg: string} class JSONResponse implements SuccessResponse { constructor(public data: string) { } } function assertResponse(obj: SuccessResponse | ErrorResponse): asserts obj is ErrorResponse { if (!(obj instanceof JSONResponse)) { throw new Error("Not a success!") } } const res = getResponse(); res // SuccessResponse | ErrorResponse assertResponse(res) // 断言函数更改当前作用域 res // ErrorResponse
赋值(Assignment)
使用 "as const" 缩小类型
对象中的属性被视为可变的,在赋值过程中,类型将被“拓宽”为非字面量类型。前缀“as const”将所有类型锁定为它们的字面量类型。
const data1 = { name: "Zagreus" } const data2 = { name: "Zagreus" } as const // data1: {name: string} // data2: { readonly name: "Zagreus"}
跟踪相关变量
class SuccessResponse { } const response = getResponse() const isSuccessResponse = response instanceof SuccessResponse if (isSuccessResponse) { response // SuccessResponse }
重新赋值更新类型
let data: string | number = Math.random() ? "asd" : 123 data // string | number data = "hello" data // string
感谢观看,欢迎互相讨论与指导,以下是参考资料链接????