TypeScript高级类型编程实战:从基础到类型体操
TypeScript的类型系统是其最强大的特性之一。除了基本的类型注解,TypeScript还提供了丰富的高级类型特性,可以让我们在编译时捕获更多错误,编写更安全的代码。本文将深入探讨TypeScript的高级类型编程技巧。
1. 基础类型工具
首先回顾一些基础但强大的类型工具:
// 1. 条件类型
type IsString = T extends string ? true : false;
type Test1 = IsString<"hello">; // true
type Test2 = IsString; // false
// 2. 映射类型
interface User {
id: number;
name: string;
email: string;
}
// 将所有属性变为可选
type PartialUser = Partial;
// 等价于 { id?: number; name?: string; email?: string; }
// 将所有属性变为只读
type ReadonlyUser = Readonly;
// 等价于 { readonly id: number; readonly name: string; readonly email: string; }
// 3. 索引访问类型
type UserId = User["id"]; // number
type UserKeys = keyof User; // "id" | "name" | "email"
// 4. 模板字面量类型
type EventName = "click" | "hover" | "submit";
type HandlerName = `on${Capitalize}`;
// "onClick" | "onHover" | "onSubmit"
2. 实用工具类型进阶
// 1. 提取函数返回类型
type ReturnType any> =
T extends (...args: any) => infer R ? R : never;
function getUser() {
return { id: 1, name: "John" };
}
type UserReturn = ReturnType;
// { id: number; name: string }
// 2. 提取函数参数类型
type Parameters any> =
T extends (...args: infer P) => any ? P : never;
function createUser(id: number, name: string) {
return { id, name };
}
type CreateUserParams = Parameters;
// [number, string]
// 3. 构造器类型
type ConstructorParameters any> =
T extends new (...args: infer P) => any ? P : never;
type InstanceType any> =
T extends new (...args: any) => infer R ? R : never;
class UserClass {
constructor(public id: number, public name: string) {}
}
type UserConstructorParams = ConstructorParameters;
// [number, string]
type UserInstance = InstanceType;
// UserClass
3. 条件类型与推断
// 1. 分布式条件类型
type ToArray = T extends any ? T[] : never;
type StringOrNumberArray = ToArray;
// string[] | number[]
// 2. 过滤类型
type Filter = T extends U ? T : never;
type StringOnly = Filter;
// string
// 3. 排除类型
type Exclude = T extends U ? never : T;
type NonString = Exclude;
// number | boolean
// 4. 提取类型
type Extract = T extends U ? T : never;
type StringOrNumber = Extract;
// string | number
// 5. 非空类型
type NonNullable = T extends null | undefined ? never : T;
type ValidValue = NonNullable;
// string
4. 递归类型与深度操作
// 1. 深度Partial
type DeepPartial = {
[P in keyof T]?: T[P] extends object ? DeepPartial : T[P];
};
interface NestedObject {
user: {
id: number;
profile: {
name: string;
age: number;
};
};
settings: {
theme: string;
notifications: boolean;
};
}
type PartialNested = DeepPartial;
// 所有层级都变为可选
// 2. 深度Readonly
type DeepReadonly = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly : T[P];
};
// 3. 深度Required
type DeepRequired = {
[P in keyof T]-?: T[P] extends object ? DeepRequired : T[P];
};
// 4. 类型安全的路径访问
type PathImpl =
K extends string
? T[K] extends object
? `${K}.${PathImpl}`
: K
: never;
type Path = PathImpl | keyof T;
type UserPaths = Path;
// "user" | "settings" | "user.id" | "user.profile" | "user.profile.name" | "user.profile.age" | "settings.theme" | "settings.notifications"
// 5. 根据路径获取类型
type Get =
P extends `${infer K}.${infer Rest}`
? K extends keyof T
? Get
: never
: P extends keyof T
? T[P]
: never;
type UserProfileName = Get; // string
type SettingsTheme = Get; // string
5. 类型体操实战
// 1. 实现Promise.all的类型安全版本
declare function PromiseAll(
values: readonly [...T]
): Promise<{
[K in keyof T]: T[K] extends Promise ? R : T[K];
}>;
// 使用示例
const results = PromiseAll([
Promise.resolve(1),
Promise.resolve("hello"),
true
]);
// Promise<[number, string, boolean]>
// 2. 实现柯里化函数的类型
type Curry any> =
F extends (...args: infer A) => infer R
? A extends [infer First, ...infer Rest]
? (arg: First) => Curry<(...args: Rest) => R>
: R
: never;
function curry any>(fn: F): Curry {
return function curried(...args: any[]): any {
if (args.length >= fn.length) {
return fn(...args);
}
return (...moreArgs: any[]) => curried(...args, ...moreArgs);
} as Curry;
}
// 使用示例
function add(a: number, b: number, c: number): number {
return a + b + c;
}
const curriedAdd = curry(add);
// (arg: number) => (arg: number) => (arg: number) => number
const add5 = curriedAdd(5);
// (arg: number) => (arg: number) => number
const add5And10 = add5(10);
// (arg: number) => number
const result = add5And10(15); // 30
// 3. 实现路由参数解析
type ParseRouteParams =
T extends `${infer Start}:${infer Param}/${infer Rest}`
? { [K in Param | keyof ParseRouteParams]: string }
: T extends `${infer Start}:${infer Param}`
? { [K in Param]: string }
: {};
// 使用示例
type BlogPostParams = ParseRouteParams<"/posts/:id">;
// { id: string }
type UserProfileParams = ParseRouteParams<"/users/:userId/posts/:postId">;
// { userId: string; postId: string }
// 4. 实现类型安全的EventEmitter
type EventMap = {
click: { x: number; y: number };
change: { value: string };
submit: { data: Record };
};
class TypedEventEmitter> {
private listeners: Map void>> = new Map();
on(event: K, listener: (data: T[K]) => void): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(listener);
}
emit(event: K, data: T[K]): void {
const listeners = this.listeners.get(event);
if (listeners) {
listeners.forEach(listener => listener(data));
}
}
}
// 使用示例
const emitter = new TypedEventEmitter();
emitter.on("click", (data) => {
console.log(`Clicked at (${data.x}, ${data.y})`);
// data的类型自动推断为 { x: number; y: number }
});
emitter.emit("click", { x: 100, y: 200 });
// emitter.emit("click", { x: 100 }); // 错误:缺少y属性
6. 实用类型模式
// 1. 品牌类型(Branded Types)
type Brand = K & { readonly __brand: T };
type UserId = Brand;
type ProductId = Brand;
function createUserId(id: number): UserId {
return id as UserId;
}
function createProductId(id: number): ProductId {
return id as ProductId;
}
const userId = createUserId(123);
const productId = createProductId(456);
// 以下代码会在编译时报错
// function processUser(id: UserId) { /* ... */ }
// processUser(productId); // 错误:类型不匹配
// 2. 名义类型(Nominal Types)
declare const nominalType: unique symbol;
type Nominal = T & {
readonly [nominalType]: K;
};
type Email = Nominal;
type Phone = Nominal;
function validateEmail(email: string): Email | null {
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
return email as Email;
}
return null;
}
// 3. 类型安全的枚举替代方案
const UserRole = {
ADMIN: "admin",
USER: "user",
GUEST: "guest"
} as const;
type UserRole = typeof UserRole[keyof typeof UserRole];
// "admin" | "user" | "guest"
// 4. 类型安全的配置对象
type Config = {
api: {
baseUrl: string;
timeout: number;
};
features: {
darkMode: boolean;
analytics: boolean;
};
};
function createConfig(config: T): T {
return config;
}
const config = createConfig({
api: {
baseUrl: "https://api.example.com",
timeout: 5000
},
features: {
darkMode: true,
analytics: false
}
});
// 类型安全,自动推断
总结
TypeScript的高级类型系统提供了强大的工具,可以让我们在编译时捕获更多错误,编写更安全的代码。通过掌握这些高级类型技巧,我们可以:
- 创建更精确的类型约束,减少运行时错误
- 实现类型安全的API和库
- 提高代码的可维护性和可读性
- 利用类型系统进行文档化
- 实现复杂的类型转换和验证
在实际项目中,建议:
- 从简单的工具类型开始,逐步学习复杂类型
- 使用类型体操解决实际问题,而不是为了炫技
- 保持类型的简洁性,避免过度复杂的类型定义
- 利用TypeScript的类型推断,减少显式类型注解
- 定期重构类型定义,保持类型系统的整洁
通过合理运用TypeScript的高级类型特性,我们可以构建出更健壮、更易维护的应用程序。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END




暂无评论内容