TypeScript高级类型编程:从泛型到模板字面量类型
TypeScript的类型系统是图灵完备的。掌握高级类型编程,可以让你编写更安全、更优雅的代码。
条件类型
条件类型允许你根据类型关系进行分支判断:
typescript
// 基础条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<42>; // false
// 实用模式:提取Promise返回值
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type Result = Unwrap<Promise<string>>; // stringinfer关键字
infer 在条件类型中推断类型变量,是高级类型编程的核心工具:
typescript
// 提取函数返回类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
// 提取Promise链中的最深类型
type DeepUnwrap<T> = T extends Promise<infer U>
? DeepUnwrap<U>
: T;模板字面量类型
TypeScript 4.1引入的模板字面量类型开启了字符串类型编程的大门:
typescript
// 生成事件名类型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<'click'>; // 'onClick'
type FocusEvent = EventName<'focus'>; // 'onFocus'
// CSS单位类型约束
type CSSValue = `${number}${'px' | 'rem' | 'em' | '%'}`;
const width: CSSValue = '100px'; // OK
// const bad: CSSValue = '100'; // Error!映射类型
typescript
// 将所有属性变为只读
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
// Pick和Omit的类型级实现
type MyPick<T, K extends keyof T> = { [P in K]: T[P] };
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;品牌类型(Branded Types)
防止原始类型的意外混用:
typescript
declare const brand: unique symbol;
type Brand<T, B> = T & { [brand]: B };
type Email = Brand<string, 'Email'>;
type UserId = Brand<string, 'UserId'>;
function createEmail(email: string): Email {
if (!email.includes('@')) throw new Error('Invalid email');
return email as Email;
}
function sendEmail(email: Email, userId: UserId) {}
const email = createEmail('test@example.com');
// sendEmail('test@example.com', '123'); // Type error!分发条件类型
联合类型在条件类型中自动分发:
typescript
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>;
// string[] | number[] — 每个成员分别应用
// 阻止分发
type ToArrayOne<T> = [T] extends [any] ? T[] : never;
type Result2 = ToArrayOne<string | number>;
// (string | number)[]实战案例:类型安全的路由构建器
typescript
type Route = {
home: { params: {} };
user: { params: { id: string } };
post: { params: { slug: string; tab?: 'comments' | 'likes' } };
};
type ExtractParams<T> = T extends string
? T extends `:${infer P}`
? P
: never
: never;
function navigate<T extends keyof Route>(
route: T,
params: Route[T]['params']
) { /* ... */ }
navigate('user', { id: '123' });
navigate('post', { slug: 'hello', tab: 'comments' });总结
TypeScript的类型系统远不止 string 和 number。掌握高级类型编程,可以让类型成为你的文档、测试和合约,在编译时捕获更多错误。