类型守卫是一种在运行时检查类型的表达式,用于在特定的作用域内缩小类型范围,让TypeScript能够更精确地推断类型。
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
// 在这个代码块中,padding被缩小为number类型
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
// 在这个代码块中,padding被缩小为string类型
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
// typeof只能识别以下类型
// "string", "number", "bigint", "boolean", "symbol", "undefined", "object", "function"
class Bird {
fly() {
console.log("Flying...");
}
layEggs() {
console.log("Laying eggs...");
}
}
class Fish {
swim() {
console.log("Swimming...");
}
layEggs() {
console.log("Laying eggs...");
}
}
function getRandomPet(): Bird | Fish {
return Math.random() > 0.5 ? new Bird() : new Fish();
}
let pet = getRandomPet();
if (pet instanceof Bird) {
pet.fly(); // 正确:pet被缩小为Bird类型
}
if (pet instanceof Fish) {
pet.swim(); // 正确:pet被缩小为Fish类型
}
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function move(pet: Bird | Fish) {
if ("fly" in pet) {
// pet被缩小为Bird类型
pet.fly();
} else {
// pet被缩小为Fish类型
pet.swim();
}
}
使用类型谓词 parameterName is Type 创建自定义类型守卫:
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
// 自定义类型守卫函数
function isFish(pet: Bird | Fish): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Bird | Fish) {
if (isFish(pet)) {
pet.swim(); // pet被缩小为Fish类型
} else {
pet.fly(); // pet被缩小为Bird类型
}
}
使用共同的字面量类型属性来区分联合类型:
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape): number {
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.width * s.height;
case "circle":
return Math.PI * s.radius ** 2;
}
}
使用never类型确保所有情况都被处理:
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function area(s: Shape): number {
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.width * s.height;
case "circle":
return Math.PI * s.radius ** 2;
default:
return assertNever(s); // 如果有遗漏的case,这里会报错
}
}
// 尖括号语法
let someValue: any = "this is a string";
let strLength: number = (someValue).length;
// as语法(推荐,在JSX中只能使用这种)
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;
// 非空断言操作符
function liveDangerously(x?: number | null) {
console.log(x!.toFixed()); // 断言x不是null或undefined
}
// 有时需要先断言为any或unknown,再断言为目标类型
const a = (expr as any) as T;
const b = (expr as unknown) as T;