TypeScript 高级类型编程实战
(7 个月前)
Nick Chen
约 5 分钟
TypeScript 的类型系统不仅仅是为了添加类型注解,它实际上是一个功能强大的编程语言。本文将带你深入探索 TypeScript 的高级类型编程技巧。
条件类型 (Conditional Types)
条件类型是 TypeScript 类型系统中最强大的功能之一:
// 基础条件类型
type IsArray<T> = T extends any[] ? true : false;
type Test1 = IsArray<string[]>; // true
type Test2 = IsArray<string>; // false
// 更复杂的条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type SafeString = NonNullable<string | null>; // string
分布式条件类型
当条件类型作用于联合类型时,会产生分布式效果:
type ToArray<T> = T extends any ? T[] : never;
type ArrayUnion = ToArray<string | number>; // string[] | number[]
映射类型 (Mapped Types)
映射类型允许你基于现有类型创建新类型:
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 自定义映射类型
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface User {
name: string;
age: number;
}
type UserGetters = Getters<User>;
// {
// getName: () => string;
// getAge: () => number;
// }
键重映射
TypeScript 4.1 引入了键重映射功能:
type EventHandlers<T> = {
[P in keyof T as `on${Capitalize<string & P>}Changed`]: (value: T[P]) => void;
};
type UserEventHandlers = EventHandlers<User>;
// {
// onNameChanged: (value: string) => void;
// onAgeChanged: (value: number) => void;
// }
模板字面量类型
模板字面量类型提供了强大的字符串操作能力:
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
// 实际应用:API 路径类型
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type APIPath = '/users' | '/posts' | '/comments';
type APIEndpoint = `${HTTPMethod} ${APIPath}`;
// "GET /users" | "POST /users" | ... 等 12 种组合
字符串操作工具类型
// 内置字符串操作类型
type UppercaseGreeting = Uppercase<"hello world">; // "HELLO WORLD"
type LowercaseGreeting = Lowercase<"HELLO WORLD">; // "hello world"
type CapitalizedGreeting = Capitalize<"hello world">; // "Hello world"
type UncapitalizedGreeting = Uncapitalize<"Hello World">; // "hello World"
递归类型
TypeScript 支持递归类型定义:
// 深度只读类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// JSON 类型
type JSONValue =
| string
| number
| boolean
| null
| JSONObject
| JSONArray;
interface JSONObject {
[key: string]: JSONValue;
}
interface JSONArray extends Array<JSONValue> {}
// 路径类型(支持嵌套对象)
type PathImpl<T, K extends keyof T> = K extends string
? T[K] extends Record<string, any>
? T[K] extends ArrayLike<any>
? K | `${K}.${PathImpl<T[K], Exclude<keyof T[K], keyof any[]>>}`
: K | `${K}.${PathImpl<T[K], keyof T[K]>}`
: K
: never;
type Path<T> = PathImpl<T, keyof T> | keyof T;
// 使用示例
interface NestedObject {
user: {
profile: {
name: string;
settings: {
theme: string;
};
};
};
posts: Array<{
title: string;
content: string;
}>;
}
type ValidPaths = Path<NestedObject>;
// "user" | "posts" | "user.profile" | "user.profile.name" | "user.profile.settings" | "user.profile.settings.theme"
工具类型实战
创建一个类型安全的事件发射器
// 定义事件映射
interface EventMap {
'user:login': { userId: string; timestamp: number };
'user:logout': { userId: string };
'post:created': { postId: string; authorId: string };
'post:deleted': { postId: string };
}
// 类型安全的事件发射器
class TypedEventEmitter<T extends Record<string, any>> {
private listeners: {
[K in keyof T]?: Array<(data: T[K]) => void>;
} = {};
on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
const eventListeners = this.listeners[event];
if (eventListeners) {
eventListeners.forEach(listener => listener(data));
}
}
off<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
const eventListeners = this.listeners[event];
if (eventListeners) {
const index = eventListeners.indexOf(listener);
if (index > -1) {
eventListeners.splice(index, 1);
}
}
}
}
// 使用
const emitter = new TypedEventEmitter<EventMap>();
emitter.on('user:login', (data) => {
// data 的类型是 { userId: string; timestamp: number }
console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});
// 类型检查:这会产生编译错误
// emitter.emit('user:login', { userId: 'abc' }); // Error: timestamp 缺失
函数重载的类型安全实现
// 函数重载类型
type Overload = {
(x: string): string;
(x: number): number;
(x: boolean): boolean;
};
// 使用条件类型实现
type SafeOverload<T> = T extends string
? string
: T extends number
? number
: T extends boolean
? boolean
: never;
function safeConvert<T extends string | number | boolean>(x: T): SafeOverload<T> {
if (typeof x === 'string') {
return x.toUpperCase() as SafeOverload<T>;
}
if (typeof x === 'number') {
return (x * 2) as SafeOverload<T>;
}
if (typeof x === 'boolean') {
return !x as SafeOverload<T>;
}
throw new Error('Unsupported type');
}
const result1 = safeConvert('hello'); // string
const result2 = safeConvert(42); // number
const result3 = safeConvert(true); // boolean
性能注意事项
在使用高级类型时,需要注意以下几点:
- 避免过度复杂的递归类型:可能导致编译器性能问题
- 合理使用条件类型:过多的嵌套条件可能影响 IDE 性能
- 缓存复杂的类型计算:使用类型别名避免重复计算
// 不好的做法
type BadExample<T> = T extends object
? T extends Array<infer U>
? Array<BadExample<U>>
: { [K in keyof T]: BadExample<T[K]> }
: T;
// 更好的做法
type ProcessedObject<T> = { [K in keyof T]: Transform<T[K]> };
type ProcessedArray<T> = T extends Array<infer U> ? Array<Transform<U>> : never;
type Transform<T> = T extends object
? T extends any[]
? ProcessedArray<T>
: ProcessedObject<T>
: T;
实际应用案例
表单验证类型
type ValidationRule<T> = {
required?: boolean;
minLength?: T extends string ? number : never;
maxLength?: T extends string ? number : never;
min?: T extends number ? number : never;
max?: T extends number ? number : never;
pattern?: T extends string ? RegExp : never;
};
type FormSchema<T> = {
[K in keyof T]: ValidationRule<T[K]>;
};
interface UserForm {
username: string;
age: number;
email: string;
}
const userFormSchema: FormSchema<UserForm> = {
username: {
required: true,
minLength: 3,
maxLength: 20
},
age: {
required: true,
min: 18,
max: 120
},
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
}
};
总结
TypeScript 的高级类型系统为我们提供了强大的工具来创建更安全、更灵活的代码。通过掌握条件类型、映射类型、模板字面量类型等高级特性,我们可以:
- 创建更精确的类型定义
- 提高代码的类型安全性
- 减少运行时错误
- 改善开发体验
记住,类型编程的目标是让代码更安全和更易维护,而不是炫技。合理使用这些高级特性,会让你的 TypeScript 代码更上一层楼!
推荐阅读: