类型细化
类型细化是指TypeScript编译器通过分析特定的代码结构,从而得出代码中特定位置上表达式的具体类型
类型细化的方式
- 类型守卫
- 可辨识联合类型
- 赋值语句
- 控制流语句
- 断言函数
类型守卫
typeof
typeof运算符用于获取操作数的数据类型
typeof类型守卫能够根据typeof表达式的值去细化typeof操作数的类型
function f(x: number[] | undefined | null) {
if (typeof x === 'object') {
x; // number[] | null
} else {
x; // undefined
}
}
instanceof类型守卫
instanceof运算符能够检测实例对象与构造函数之间的关系
instanceof类型守卫会根据instanceof运算符的返回值将左操作数的类型进行细化
function f(x: Date | RegExp) {
if (x instanceof Date) {
x; // Date
}
if (x instanceof RegExp) {
x; // RegExp
}
}
in类型守卫
in运算符是JavaScript中的关系运算符之一,用来判断对象自身或其原型链中是否存在给定的属性,若存在则返回true,否则返回false
in类型守卫根据in运算符的测试结果,将右操作数的类型细化为具体的对象类型
interface A {
x: number;
}
interface B {
y: string;
}
function f(x: A | B) {
if ('x' in x) {
x; // A
} else {
x; // B
}
}
逻辑与、或、非类型守卫
逻辑表达式在求值时会判断操作数的真与假。如果一个值转换为布尔值后为true,那么该值为真值;如果一个值转换为布尔值后为false,那么该值为假值
逻辑非类型守卫将根据逻辑非表达式的结果对操作数进行类型细化
逻辑与类型守卫将根据逻辑与表达式的结果对操作数进行类型细化
逻辑或类型守卫将根据逻辑或表达式的结果对操作数进行类型细化
function f(x: true | false | 0 | 0n | '' | undefined | null)
{
if (x) {
x; // true
} else {
x; // false | 0 | 0n | '' | undefined | null
}
}
等式类型守卫
等式表达式可以使用四种等式运算符===、!==\、==、!=,它们能够将两个值进行相等性比较并返回一个布尔值。
编译器能够对等式表达式进行分析,从而将等式运算符的操作数进行类型细化
function f0(x: boolean | undefined) {
if (x === undefined) {
x; // undefined
} else {
x; // boolean
}
if (x !== undefined) {
x; // boolean
} else {
x; // undefined
}
}
function f1(x: boolean | null) {
if (x === null) {
x; // null
} else {
x; // boolean
}
}
if (x !== null) {
x; // boolean
} else {
x; // null
}
}
自定义类型守卫函数
类型守卫函数是指在函数返回值类型中使用了类型谓词的函数
类型谓词表示一种类型判定,即判定x的类型是否为T
类型谓词的语法:
x is T
type A = { a: string };
type B = { b: string };
function isTypeA(x: A | B): x is A {
return (x as A).a !\= undefined;
}
function isTypeB(x: A | B): x is B {
return (x as B).b !== undefined;
}
function f(x: A | B) {
if (isTypeA(x)) {
x; // A
} else {
x; // B
}
if (isTypeB(x)) {
x; // B
} else {
x; // A
}
}
this类型守卫
在类型谓词“x is T”中,x可以为关键字this,这时它叫作this类型守卫。
this类型守卫主要用于类和接口中,它能够将方法调用对象的类型细化为T类型
可辨识联合类型
通过结合使用联合类型、单元类型和类型守卫能够创建出一种高级应用模式,这称作可辨识联合
可辨识联合也叫作标签联合或变体类型,是一种数据结构,该数据结构中存储了一组数量固定且种类不同的类型,还存在一个标签字段,该标签字段用于标识可辨识联合中当前被选择的类型,在同一时刻只有一种类型会被选中
判别式属性
对于可辨识联合类型整体来讲,其判别式属性的类型是一个联合类型,该联合类型的成员类型是由每一个可辨识对象类型中该判别式属性的类型所组成。TypeScript要求在判别式属性的联合类型中至少有一个单元类型
判别式属性类型守卫
判别式属性类型守卫表达式支持以下几种形式:
▪x.p
▪!x.p
▪x.p == v
▪x.p === v
▪x.p != v
▪x.p !== v
赋值语句分析
TypeScript编译器能够分析代码中的赋值语句,并根据等号右侧操作数的类型去细化左侧操作数的类型。
例如,当给变量赋予一个字符串值时,编译器可以将该变量的类型细化为string类型.
基于控制流的类型分析
ypeScript编译器能够分析程序代码中所有可能的执行路径,从而得到在代码中某一特定位置上的变量类型和参数类型等,我们将这种类型分析方式叫作基于控制流的类型分析
常用的控制流语句有if语句、switch语句以及return语句等。
在使用类型守卫时,我们已经在使用基于控制流的类型分析了
断言函数
断言函数用于检查实际参数的类型是否符合类型判定。
若符合类型判定,则函数正常返回;
若不符合类型判定,则函数抛出异常。
基于控制流的类型分析能够识别断言函数并进行类型细化
断言函数有以下两种形式
function assert(x: unknown): asserts x is T { }
function assert(x: unknown): asserts x { }