TypeScript 已成为现代前端开发的标配。本文将带你深入理解 TypeScript 的高级类型系统,掌握类型体操的核心技巧。
为什么需要高级类型?
基础的类型注解只是冰山一角。真正发挥 TypeScript 威力的是它强大的类型系统:
- 类型安全 - 在编译期捕获潜在错误
- 代码智能提示 - IDE 可以提供精确的补全
- 自文档化 - 类型就是最好的文档
- 重构安全 - 改动会立即暴露问题
泛型:类型的函数
泛型是类型编程的基础,就像函数是值编程的基础一样:
// 基础泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在可以安全访问 .length
return arg;
}
// 多个类型参数
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
泛型工具类型
TypeScript 内置了许多实用的工具类型:
// Partial - 所有属性可选
type PartialUser = Partial<User>;
// Required - 所有属性必需
type RequiredUser = Required<User>;
// Pick - 选取部分属性
type UserBasic = Pick<User, 'id' | 'name'>;
// Omit - 排除部分属性
type UserWithoutPassword = Omit<User, 'password'>;
// Record - 构造对象类型
type UserRoles = Record<string, string[]>;
条件类型:类型的 if-else
条件类型让类型可以根据条件进行判断:
// 基础条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 实用示例:提取 Promise 的内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type Result = UnwrapPromise<Promise<string>>; // string
// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
infer 关键字
infer 用于在条件类型中推断类型变量:
// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
// 提取构造函数实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never;
// 提取 Promise 嵌套类型
type DeepUnwrap<T> = T extends Promise<infer U>
? DeepUnwrap<U>
: T;
type Nested = Promise<Promise<Promise<string>>>;
type Flat = DeepUnwrap<Nested>; // string
模板字面量类型
TypeScript 4.1 引入的强大特性:
// 基础模板字面量
type Greeting = `Hello, ${string}!`;
const g1: Greeting = "Hello, World!"; // ✅
const g2: Greeting = "Hi, World!"; // ❌
// 组合字面量类型
type Color = 'red' | 'green' | 'blue';
type Size = 'small' | 'medium' | 'large';
type Style = `${Size}-${Color}`;
// 'small-red' | 'small-green' | ... 共 9 种组合
// 实用工具类型
type Uppercase<S extends string> = intrinsic;
type Lowercase<S extends string> = intrinsic;
type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;
// 事件处理器类型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickHandler = EventName<'click'>; // 'onClick'
实战:构建类型安全的 API 客户端
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = '/users' | '/posts' | '/comments';
type APIRoute = `${HTTPMethod} ${Endpoint}`;
// 'GET /users' | 'GET /posts' | 'POST /users' | ...
interface APIConfig {
'GET /users': { response: User[] };
'GET /posts': { response: Post[] };
'POST /users': { body: CreateUser; response: User };
}
function request<T extends keyof APIConfig>(
route: T,
config?: APIConfig[T] extends { body: infer B } ? { body: B } : never
): Promise<APIConfig[T]['response']> {
// 实现...
}
映射类型:批量变换
映射类型可以基于旧类型创建新类型:
// 基础映射
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Optional<T> = {
[P in keyof T]?: T[P];
};
// 键重映射 (TypeScript 4.1+)
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }
// 过滤属性
type FilterByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never]: T[P];
};
type StringProps = FilterByType<Person, string>;
// { name: string; }
类型守卫与类型收窄
类型守卫帮助 TypeScript 在运行时收窄类型:
// typeof 守卫
function processValue(value: string | number) {
if (typeof value === 'string') {
return value.toUpperCase(); // TypeScript 知道这是 string
}
return value.toFixed(2); // TypeScript 知道这是 number
}
// instanceof 守卫
class Dog { bark() {} }
class Cat { meow() {} }
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
// 自定义类型守卫
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
// in 操作符守卫
function move(animal: Fish | Bird) {
if ('swim' in animal) {
animal.swim();
} else {
animal.fly();
}
}
实战技巧
1. 严格的对象字面量检查
interface Config {
host: string;
port: number;
}
// 使用泛型确保精确匹配
function createConfig<T extends Config>(
config: T & Record<Exclude<keyof T, keyof Config>, never>
): Config {
return config;
}
createConfig({ host: 'localhost', port: 3000 }); // ✅
createConfig({ host: 'localhost', port: 3000, extra: 1 }); // ❌
2. 深度 Readonly
type DeepReadonly<T> = T extends (infer U)[]
? ReadonlyArray<DeepReadonly<U>>
: T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;
interface NestedObject {
a: {
b: {
c: string;
};
};
}
type ImmutableNested = DeepReadonly<NestedObject>;
// 所有层级都是 readonly
3. 类型安全的事件发射器
type EventMap = {
connect: { host: string };
disconnect: { reason: string };
message: { content: string; from: string };
};
class TypedEmitter<T extends Record<string, any>> {
on<K extends keyof T>(event: K, handler: (payload: T[K]) => void): void {
// 实现
}
emit<K extends keyof T>(event: K, payload: T[K]): void {
// 实现
}
}
const emitter = new TypedEmitter<EventMap>();
emitter.on('connect', ({ host }) => console.log(host)); // ✅ 类型正确
emitter.emit('message', { content: 'hi', from: 'user' }); // ✅
总结
- 泛型 - 类型编程的基础,实现类型的参数化
- 条件类型 - 类型层面的逻辑判断,配合
infer提取类型 - 模板字面量 - 字符串类型的强大操作能力
- 映射类型 - 批量变换对象类型的属性
- 类型守卫 - 运行时类型收窄,确保类型安全
掌握这些高级特性,你就能写出既类型安全又灵活的 TypeScript 代码。记住:类型系统是你的朋友,善用它来捕获 bug。