TypeScript联合类型与交叉类型 #
一、联合类型 #
1.1 什么是联合类型 #
联合类型表示一个值可以是多种类型之一,使用|符号连接多个类型。
typescript
type ID = string | number;
type Status = 'pending' | 'approved' | 'rejected';
let id: ID = 'abc123';
id = 123;
let status: Status = 'pending';
status = 'approved';
1.2 基本用法 #
typescript
function printId(id: string | number): void {
console.log(`ID: ${id}`);
}
printId('abc');
printId(123);
printId(true);
1.3 联合类型的成员 #
typescript
type Primitive = string | number | boolean | null | undefined | symbol | bigint;
type Result = string | number | boolean | null;
二、联合类型收窄 #
2.1 typeof收窄 #
typescript
function printValue(value: string | number): void {
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
2.2 字面量收窄 #
typescript
type Direction = 'up' | 'down' | 'left' | 'right';
function move(direction: Direction): void {
switch (direction) {
case 'up':
console.log('Moving up');
break;
case 'down':
console.log('Moving down');
break;
case 'left':
console.log('Moving left');
break;
case 'right':
console.log('Moving right');
break;
}
}
2.3 in操作符收窄 #
typescript
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function move(animal: Bird | Fish): void {
if ('fly' in animal) {
animal.fly();
} else {
animal.swim();
}
}
2.4 instanceof收窄 #
typescript
class Dog {
bark(): void {
console.log('Woof!');
}
}
class Cat {
meow(): void {
console.log('Meow!');
}
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
2.5 自定义类型守卫 #
typescript
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): void {
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
}
三、联合类型应用 #
3.1 多态参数 #
typescript
function formatValue(value: string | number | boolean): string {
if (typeof value === 'string') {
return value;
} else if (typeof value === 'number') {
return value.toFixed(2);
} else {
return value ? 'true' : 'false';
}
}
3.2 可选参数 #
typescript
function greet(name: string | undefined): string {
if (name === undefined) {
return 'Hello, Guest!';
}
return `Hello, ${name}!`;
}
greet('Alice');
greet(undefined);
3.3 函数重载 #
typescript
function createElement(tag: 'a'): HTMLAnchorElement;
function createElement(tag: 'canvas'): HTMLCanvasElement;
function createElement(tag: 'table'): HTMLTableElement;
function createElement(tag: string): HTMLElement {
return document.createElement(tag);
}
3.4 事件处理 #
typescript
type MouseEvent = {
type: 'click' | 'dblclick';
x: number;
y: number;
};
type KeyboardEvent = {
type: 'keydown' | 'keyup';
key: string;
code: string;
};
type Event = MouseEvent | KeyboardEvent;
function handleEvent(event: Event): void {
switch (event.type) {
case 'click':
case 'dblclick':
console.log(`Mouse at (${event.x}, ${event.y})`);
break;
case 'keydown':
case 'keyup':
console.log(`Key: ${event.key}`);
break;
}
}
四、交叉类型 #
4.1 什么是交叉类型 #
交叉类型将多个类型合并为一个类型,使用&符号连接多个类型。
typescript
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;
const person: Person = {
name: 'Alice',
age: 25
};
4.2 基本用法 #
typescript
interface BusinessPartner {
name: string;
credit: number;
}
interface Identity {
id: number;
name: string;
}
interface Contact {
email: string;
phone: string;
}
type Employee = Identity & Contact;
type Customer = BusinessPartner & Contact;
const employee: Employee = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
phone: '123-456-7890'
};
4.3 交叉类型的合并 #
typescript
type A = {
a: string;
};
type B = {
b: number;
};
type C = A & B & {
c: boolean;
};
const obj: C = {
a: 'hello',
b: 42,
c: true
};
五、交叉类型应用 #
5.1 混入模式 #
typescript
type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActive = false;
activate() { this.isActive = true; }
deactivate() { this.isActive = false; }
};
}
class User {
constructor(public name: string) {}
}
const TimestampedUser = Timestamped(User);
const ActivatableUser = Activatable(TimestampedUser);
const user = new ActivatableUser('Alice');
user.activate();
console.log(user.timestamp);
5.2 扩展配置 #
typescript
interface BaseConfig {
apiUrl: string;
timeout: number;
}
interface AuthConfig {
apiKey: string;
secretKey: string;
}
interface LogConfig {
logLevel: 'debug' | 'info' | 'warn' | 'error';
logFile?: string;
}
type AppConfig = BaseConfig & AuthConfig & LogConfig;
const config: AppConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000,
apiKey: 'abc123',
secretKey: 'xyz789',
logLevel: 'info'
};
5.3 组合函数类型 #
typescript
type Loggable = {
log(message: string): void;
};
type Serializable = {
serialize(): string;
};
type Entity = Loggable & Serializable;
class User implements Entity {
constructor(public name: string, public age: number) {}
log(message: string): void {
console.log(`[${new Date().toISOString()}] ${message}`);
}
serialize(): string {
return JSON.stringify({ name: this.name, age: this.age });
}
}
六、联合与交叉组合 #
6.1 优先级 #
交叉类型的优先级高于联合类型:
typescript
type A = { a: string };
type B = { b: number };
type C = { c: boolean };
type Union = A | B & C;
type Union2 = A | (B & C);
type Intersection = A & B | C;
type Intersection2 = (A & B) | C;
6.2 组合使用 #
typescript
type Success = {
status: 'success';
data: string;
};
type Error = {
status: 'error';
error: string;
};
type Loading = {
status: 'loading';
};
type State = Success | Error | Loading;
type WithTimestamp<T> = T & { timestamp: number };
type TimestampedState = WithTimestamp<State>;
七、类型冲突处理 #
7.1 原始类型冲突 #
typescript
type StringAndNumber = string & number;
let value: StringAndNumber;
7.2 对象属性冲突 #
typescript
type A = {
prop: string;
};
type B = {
prop: number;
};
type C = A & B;
let obj: C = {
prop: 'hello'
};
7.3 函数类型冲突 #
typescript
type Fn1 = (x: string) => void;
type Fn2 = (x: number) => void;
type Combined = Fn1 & Fn2;
const fn: Combined = (x: string | number) => {
console.log(x);
};
八、可辨识联合 #
8.1 什么是可辨识联合 #
可辨识联合(Discriminated Unions)是一种使用公共属性来区分联合类型成员的模式。
typescript
interface Circle {
kind: 'circle';
radius: number;
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number;
}
interface Triangle {
kind: 'triangle';
base: number;
height: number;
}
type Shape = Circle | Rectangle | Triangle;
8.2 使用可辨识联合 #
typescript
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'rectangle':
return shape.width * shape.height;
case 'triangle':
return (shape.base * shape.height) / 2;
}
}
const circle: Circle = { kind: 'circle', radius: 5 };
const rectangle: Rectangle = { kind: 'rectangle', width: 10, height: 20 };
const triangle: Triangle = { kind: 'triangle', base: 10, height: 15 };
console.log(getArea(circle));
console.log(getArea(rectangle));
console.log(getArea(triangle));
8.3 穷尽性检查 #
typescript
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'rectangle':
return shape.width * shape.height;
case 'triangle':
return (shape.base * shape.height) / 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
8.4 实际应用 #
typescript
interface LoadingState {
state: 'loading';
}
interface SuccessState<T> {
state: 'success';
data: T;
}
interface ErrorState {
state: 'error';
error: Error;
}
type AsyncState<T> = LoadingState | SuccessState<T> | ErrorState;
function render<T>(state: AsyncState<T>): string {
switch (state.state) {
case 'loading':
return 'Loading...';
case 'success':
return `Data: ${JSON.stringify(state.data)}`;
case 'error':
return `Error: ${state.error.message}`;
}
}
九、实用示例 #
9.1 API响应类型 #
typescript
type SuccessResponse<T> = {
success: true;
data: T;
};
type ErrorResponse = {
success: false;
error: {
code: number;
message: string;
};
};
type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;
function handleResponse<T>(response: ApiResponse<T>): T {
if (response.success) {
return response.data;
} else {
throw new Error(response.error.message);
}
}
9.2 表单字段类型 #
typescript
type TextField = {
type: 'text';
name: string;
label: string;
placeholder?: string;
required?: boolean;
};
type NumberField = {
type: 'number';
name: string;
label: string;
min?: number;
max?: number;
};
type SelectField = {
type: 'select';
name: string;
label: string;
options: { value: string; label: string }[];
};
type FormField = TextField | NumberField | SelectField;
function renderField(field: FormField): string {
switch (field.type) {
case 'text':
return `<input type="text" name="${field.name}" placeholder="${field.placeholder || ''}" />`;
case 'number':
return `<input type="number" name="${field.name}" min="${field.min || ''}" max="${field.max || ''}" />`;
case 'select':
const options = field.options.map(o => `<option value="${o.value}">${o.label}</option>`).join('');
return `<select name="${field.name}">${options}</select>`;
}
}
十、总结 #
本章介绍了TypeScript的联合类型和交叉类型:
联合类型要点 #
- 使用
|连接多个类型 - 表示值可以是多种类型之一
- 需要类型收窄才能访问特定类型的方法
- 常用于多态参数、可选值、可辨识联合
交叉类型要点 #
- 使用
&连接多个类型 - 将多个类型合并为一个类型
- 常用于混入模式、扩展配置、组合功能
最佳实践 #
- 使用可辨识联合提高类型安全性
- 使用类型守卫进行类型收窄
- 注意交叉类型的属性冲突
- 合理组合联合类型和交叉类型
最后更新:2026-03-26