这个博客记录学习typescript类型推断相关的知识总结,根据官方文档理解翻译而来
一、typeof 操作符
typeof用于判断一个原始类型。类型列表如下:
"string""number""bigint""boolean""symbol""undefined""object""function"
因此可以根据这个操作符,确定操作对象属于哪个原始类型
二、‘真值’判断
‘真值’判断简单来说就是利用if语法会将表达式转为布尔值的特性,将if块中的变量类型范围进一步缩小。如下示例:
function printAll(strs: string | string[] | null) {
if (strs && typeof strs === "object") {
// 确定类型是string[]
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
// 确定类型是string
console.log(strs);
}
}
这儿需要注意if判断时的隐式转换规则,以下转换皆为false
0NaN""(the empty string)0n(thebigintversion of zero)nullundefined
三、‘等值’判断
‘等值’判断的原理同‘真值’判断,也是利用if语法,但可以通过变量的可能类型集合做一些额外的判断。如下示例:
function example(x: string | number, y: string | boolean) {
if (x === y) {
// x与y变量只有一个交集类型string,因此这儿只能是string类型
x.toUpperCase();
y.toLowerCase();
} else {
console.log(x);
console.log(y);
}
}
四、in 操作符
in操作符用于判断某个属性是否存在与指定对象中。因此可以通过这个特性进行类型推断,如下示例:
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
// 有'swim' 只能是Fish类型
return animal.swim();
}
return animal.fly();
}
五、instanceof 操作符
instanceof操作符是用于判断某个对象的原型链上是否存在指定对象的prototype对象(原型对象),因此也可以进行类型推断,示例如下:
function logValue(x: Date | string) {
if (x instanceof Date) {
// 此时x是Date类型
console.log(x.toUTCString());
} else {
console.log(x.toUpperCase());
}
}
六、类型断言方法
类型断言方法是一种特殊的方法语法形式,用于确定某个变量的类型范围。示例如下:
type Fish = { swim: () => void };
type Bird = { fly: () => void };
declare function getSmallPet(): Fish | Bird;
// 断言方法
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
let pet = getSmallPet();
if (isFish(pet)) {
// Fish类型
pet.swim();
} else {
pet.fly();
}
七、可辨识的联合类型(重要)
当需要依据某个属性来确定某个对象属性什么类型时,可以使用可辨识的联合类型,简要原理在联合类型的所有类型中设置一个共有属性,并且值为字面量,通过这个属性,便可以区分属于哪个类型。示例如下:
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
八、穷尽检测法(了解)
这种方法一般用于代码遗漏逻辑的检查,可以算是一种提高代码健壮性的技巧。利用never类型的特性,让typescript类型系统检测出一些未完善的逻辑。示例如下:
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;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
在上述示例中,代码绝对不会走到default分支,因此default分支的shape对象会被推断为never类型,但当代码扩展,Shape联合的不止2个类型,但在switch中又没对所有的类型进行处理,此时default分支的shape对象不会推断为nerve类型,其他类型不能复制给nerve类型的变量,因此会报错。这就给了开发者一个提示,是否丢失了某些逻辑。