类型保护
类型保护的作用是告诉编译器:“在这个 if 语句块里,我已经检查过了,这个变量一定是 string 类型,请允许我使用 string 的方法。”
这个过程在 TypeScript 中被称为 Type Narrowing (类型收窄)。
TypeScript 提供了多种方式来实现类型保护,由简入深主要分为以下几类:
- typeof
- instanceof
- in
- 自定义
- 可辨识联合
typeof
这是最常见的方式,适用于 JavaScript 的基础数据类型。
- 适用类型: string, number, boolean, symbol。
- 注意: 对于 null、Array 或自定义对象,typeof 都会返回 'object',所以它不能用来区分具体的对象类型。
function printId(id: number | string) {
if (typeof id === "string") {
// 在这个块中,TS 知道 id 是 string
console.log(id.toUpperCase());
} else {
// TS 自动推断这里 id 一定是 number
console.log(id.toFixed(2));
}
}
instanceof
当你需要检查一个变量是否是某个类的实例时使用。它通过原型链进行检查。
- 适用类型: 类(Class)、内置对象(如 Date, RegExp)。
class Dog {
bark() { console.log("Woof!"); }
}
class Cat {
meow() { console.log("Meow!"); }
}
function interact(pet: Dog | Cat) {
if (pet instanceof Dog) {
// TS 收窄 pet 为 Dog 类型
pet.bark();
} else {
// TS 收窄 pet 为 Cat 类型
pet.meow();
}
}
in
如果不同的对象类型可以通过“是否拥有某个属性”来区分,可以使用 in。
- 场景: 区分接口或纯对象结构。
interface Admin {
name: string;
privileges: string[];
}
interface Employee {
name: string;
startDate: Date;
}
function printEmployeeInfo(emp: Admin | Employee) {
// 检查 'privileges' 属性是否存在于 emp 中
if ("privileges" in emp) {
// TS 确认 emp 是 Admin
console.log("Privileges: " + emp.privileges);
} else {
// TS 确认 emp 是 Employee
console.log("Start Date: " + emp.startDate);
}
}
自定义
这是 TypeScript 最强大的功能之一。有些逻辑太复杂(例如校验一个深层嵌套的对象),TS 无法自动推断。这时我们可以编写一个返回类型为 parameter is Type 的函数。
- 语法核心: arg is Type 。
interface Fish { swim: () => void; }
interface Bird { fly: () => void; }
// 自定义保护函数
// 只有当函数返回 true 时,TS 才会认为 pet 是 Fish
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // TS 愉快地接受了
} else {
pet.fly(); // TS 推断剩下的是 Bird
}
}
可辨识联合
这是处理复杂业务逻辑最优雅的方式。通过在每个接口中强制添加一个共同的字面量字段(通常叫 kind 或 type),利用 switch 或 if 来实现绝对精确的收窄。
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":
// TS 知道这里只能是 Circle
return Math.PI * shape.radius ** 2;
case "square":
// TS 知道这里只能是 Square
return shape.sideLength ** 2;
}
}
类型推导
TypeScript 的一大核心能力就是 不写类型也能自动推导类型。分为以下几种类型推导:
- 基础类型推导
- 上下文类型推导
- infer
基础类型推导
一些基础类型,不需要写具体的类型TS也能够自动推导出来。
let num = 100; // number
const status = 'success'; // 'success'
const user = {
name: 'ErMao',
age: 18
}
// { name: string; age: number }
const arr = [1,2,3]; // number[]
const mixed = [1,'a']; // (number | string)[]
上下文类型推导
TS 根据“使用场景”推导类型。
window.addEventListener('click', (e) => {
// e → MouseEvent
});
infer
infer 让 TypeScript 通过模式匹配提取类型的一部分。
它只能用于条件类型 extends ? xx : xx 中。
type MyReturn<T> = T extends (...args: any[]) => infer R ? R : never;
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;