类型组合与类型保护
🎯 引言
学会这篇文章,你将能够理解并灵活运用 TypeScript 中的联合类型和交叉类型,掌握类型断言的使用技巧,以及通过 typeof、instanceof 和自定义类型守卫有效区分不同类型。文章还会带你深入理解字面量类型的作用,帮助你写出更安全、更高效的前端代码。
🔀 联合类型(Union Types)与交叉类型(Intersection Types)
在开始之前,我们先说明这两个类型的区别,虽然名字相似,功能却大不相同。
联合类型用 | 表示,表示变量可以是多种类型之一。举个例子,变量既可以是字符串,也可以是数字。
交叉类型用 & 表示,表示变量同时满足多个类型的要求,合并多个类型的属性。
联合类型示例
type ID = number | string;
function printId(id: ID) {
console.log('你的ID是:' + id);
}
printId(123);
printId('abc123');
这段代码中,id 参数可以是数字也可以是字符串,调用时不限制类型,只要是这两种之一就行。
交叉类型示例
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: number;
}
type EmployeePerson = Person & Employee;
const emp: EmployeePerson = {
name: '张三',
age: 30,
employeeId: 101,
};
这里 EmployeePerson 必须同时拥有 Person 和 Employee 的属性。
✨ 类型断言(as)
类型断言可以帮助你告诉编译器变量的具体类型,类似于“我知道这个变量的类型,你不用担心”。
基本用法
let someValue: unknown = '这是一段字符串';
let strLength: number = (someValue as string).length;
这里 someValue 原本是 unknown 类型,通过 as string 告诉编译器它是字符串,从而访问 length 属性不会报错。
🔍 类型守卫
类型守卫是 TypeScript 用来缩小联合类型范围的技术,帮我们安全地访问特定类型的属性或方法。
typeof 类型守卫
typeof 用于区分基础类型,如 "string", "number", "boolean"。
function double(value: number | string) {
if (typeof value === 'string') {
return value.repeat(2);
} else {
return value * 2;
}
}
这里根据 typeof 来判断是字符串还是数字,分别执行不同操作。
instanceof 类型守卫
instanceof 用于判断对象实例的类型,常见于类或构造函数。
class Dog {
bark() {
console.log('汪汪');
}
}
class Cat {
meow() {
console.log('喵喵');
}
}
function speak(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
自定义类型守卫
有时候需要手动写守卫函数,返回值是类型谓词 param is Type。
interface Fish {
swim: () => void;
}
interface Bird {
fly: () => void;
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
}
🏷️ 字面量类型(Literal Types)
字面量类型是指变量的类型限定为某个具体值,如 "left" 或 "right"。
使用场景举例
type Direction = 'left' | 'right' | 'up' | 'down';
function move(direction: Direction) {
console.log('移动方向:' + direction);
}
move('left'); // 正确
// move("forward"); // 错误,编译报错
这样就限定了参数只能是指定的几个方向,防止传入不合法的值。
在设计组件或函数时,用字面量类型限定参数是一个好习惯。
🧾 小节总结
- 联合类型用
|表示,值可以是多种类型之一,灵活处理多种输入。 - 交叉类型用
&表示,合并多个类型属性,适合多个职责合一的对象。 - 类型断言
as用来告诉编译器变量具体类型,谨慎使用避免运行错误。 typeof和instanceof是常用的类型守卫,帮助区分不同类型。- 自定义类型守卫函数提高代码可读性和安全性。
- 字面量类型限定具体值,提高了类型安全和代码质量。
❓ 知识问答(Q&A)
Q:联合类型和交叉类型的根本区别是什么?
A:联合类型表示变量可能属于多种类型之一,交叉类型则表示变量同时满足多个类型的要求。
Q:类型断言和类型转换有什么区别?
A:类型断言只是告诉编译器变量的类型,编译时有效,运行时无影响;类型转换是运行时的实际转换。
Q:什么时候需要写自定义类型守卫?
A:当 typeof 和 instanceof 不能准确识别类型时,比如接口类型的区分,就需要自定义类型守卫。
🧪 小练习
练习 1
请写一个函数,接收参数类型为 string | number | boolean,根据类型不同返回不同结果,比如字符串返回长度,数字返回平方,布尔值返回字符串 "true" 或 "false"。
function processValue(value: string | number | boolean) {
// 在这里补充代码
}
练习2
定义两个接口 Square 和 Circle,分别有边长和半径属性,写一个函数接收它们的交叉类型,返回它们的面积。
interface Square {
sideLength: number;
}
interface Circle {
radius: number;
}
type Shape = Square & Circle;
function getArea(shape: Shape) {
// 在这里补充代码
}
🎉 恭喜你已经掌握了类型组合(联合类型 & 交叉类型)、类型断言、类型守卫和字面量类型的核心技能啦!这些都是写安全、健壮 TypeScript 代码的基础武器,继续练习,前端路上更自信!
