TS 的类型是可以组合使用的 其实就是 TS 联合
窄化和类型守卫
function getValue(x: number | string, y: string) {
return new Array(x + 1).join(" ") + y; // 这个代码在ts中是会报错的 运算符“+”不能应用于类型“string | number”和“number”。ts(2365)
}
// 如果我们加上判断
function getValue(x: number | string, y: string) {
if (typeof x === "number") {
return new Array(x + 1).join(" ") + y; // 这个时候是不会报错的
}
return x + y;
}
当我们使用了if+typeof操作后,ts 可以识别变窄后的类型,成为窄化 (Narrowing) 这样可以让 ts 知道 x 当前是什么类型
在实现层面,TS 会认为 typeof x === “number” 这样的表达式是一种类型守卫 (type guard) 表达式 ,本质上就是 if + type guard∑ 实现了窄化(Narrowing)
总结:类型窄化 就是根据类型守卫在子语句块中重新定义了更加具体的类型
真值窄化
function returnValue(
values: number[] | undefined,
i: number
): number[] | undefined {
// 这个地方其实就是真值窄化
if (!values) {
return values;
} else {
return values.map((x) => x * i);
}
}
真值窄化 可以帮助我们更好的应对 null/undefined/0 等值带来的问题
相等性窄化
相等性窄化 === !== == != 都可以用来窄化类型
看个例子
function printType(x:string | string[] | null){
if(x !== null){
if(typeof x === 'object){
for(const s of x){
// x的类型就是string[]
}
}else if(typeof x === 'string'){
// x的类型就是string
}
}
}
in 操作符窄化
js 中的 in 操作符 是用来检验对象是否存在某个属性
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
这个地方没有 instanceof 是因为 type 定义的东西 没有运行时 Fish 只存在在编译的时候
instanceof 窄化
function getData(x: Date | string) {
if (x instanceof Date) {
// x 是 Date类型
} else {
// x 是string 类型
}
}
这里要注意 Date 是真实的 Function 类型 而不能是 type 定义的
控制流分析
ts 是怎么做到窄化的功能的?
首先在语法分析阶段,ts 的编译器会识别出来类型卫兵表达式,包括一些隐性的类型卫兵,比如真值表达式 instanceof
在语义分析的时候,ts 遇到控制流关键字 if/while等 就会看这里有没有需要窄化的操作
- 首先 ts 会看到一个表达式 例如
typeof x === 'number' - 然后 ts 会对返回值 做窄化
- 窄化的本质就是重新定义类型
很多语句都会触发窄化
类型断言
ts 有两个断言的操作符号 as 和 is
as 操作符是提示 ts 某种类型是什么
is 操作符是用户自定的类型守卫
判别的联合
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}
// switch case 在帮助我们做类型的窄化
Never 类型
never 就是不应该出现的值 当我们不希望代码走到这个地方 可以使用 never
窄化解决了什么问题
联合类型在使用中根据不同控制条件重定义 提升了对联合类型的校验