TypeScript 类型体操:高级类型技巧完全指南

前言

TypeScript 的类型系统是其最强大的特性之一。掌握高级类型技巧可以让我们写出更加类型安全的代码。本文将深入探讨条件类型、映射类型、模板字面量类型等高级特性。

条件类型

条件类型允许我们基于其他类型来推导类型:

1
2
3
4
type IsString<T> = T extends string ? 'yes' : 'no';

type A = IsString<string>; // 'yes'
type B = IsString<number>; // 'no'

分布式条件类型

当条件类型作用于联合类型时,会发生分布式计算:

1
2
3
4
type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArr = ToArray<string | number>;
// string[] | number[]

映射类型

映射类型允许我们从现有类型创建新类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};

type Partial<T> = {
[P in keyof T]?: T[P];
};

// 实际使用
interface User {
name: string;
age: number;
}

type ReadonlyUser = Readonly<User>;
type OptionalUser = Partial<User>;

带修饰符的映射

1
2
3
4
5
6
7
8
9
// 移除只读属性
type Writable<T> = {
-readonly [P in keyof T]: T[P];
};

// 移除可选属性
type Required<T> = {
[P in keyof T]-?: T[P];
};

模板字面量类型

TypeScript 4.1 引入了模板字面量类型:

1
2
3
4
5
6
7
type EventName = `on${Capitalize<string>}`;
type CSSUnit = `${number}px` | `${number}rem` | `${number}%`;

// 实战:自动补全 CSS 属性
type CSSProperties = {
[K in `margin-${'top' | 'right' | 'bottom' | 'left'}`]: string | number;
};

高级技巧组合

元组类型转换

1
2
3
4
5
6
7
type TupleToObject<T extends readonly any[]> = {
[K in T[number]]: K;
};

type Tuple = ['a', 'b', 'c'];
type Object = TupleToObject<Tuple>;
// { a: 'a', b: 'b', c: 'c' }

递归类型

1
2
3
4
5
6
7
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: T[P];
};

实战案例

实现一个类型安全的 EventEmitter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type EventMap = Record<string, any>;

class TypedEventEmitter<T extends EventMap> {
private listeners: Partial<{
[K in keyof T]: Set<(payload: T[K]) => void>;
}> = {};

on<K extends keyof T>(event: K, listener: (payload: T[K]) => void) {
if (!this.listeners[event]) {
this.listeners[event] = new Set();
}
this.listeners[event]!.add(listener);
}

emit<K extends keyof T>(event: K, payload: T[K]) {
this.listeners[event]?.forEach(listener => listener(payload));
}
}

// 使用
interface Events {
userLoggedIn: { userId: string; name: string };
userLoggedOut: { userId: string };
}

const emitter = new TypedEventEmitter<Events>();
emitter.on('userLoggedIn', ({ userId, name }) => {
console.log(`${name} (${userId}) 登录了`);
});

总结

TypeScript 的高级类型特性为我们提供了强大的类型推导能力。合理使用这些技巧,可以让我们的代码更加类型安全,同时保持良好的开发体验。