- 发布于
TypeScript 进阶技巧:类型体操与实战应用
- 作者

- 姓名
- 全能波
- GitHub
- @weicracker
TypeScript 进阶技巧:类型体操与实战应用
TypeScript 不仅仅是 JavaScript 的超集,它还提供了强大的类型系统。本文将深入探讨 TypeScript 的高级特性,帮助你写出更安全、更优雅的代码。
高级类型系统
1. 条件类型(Conditional Types)
条件类型是 TypeScript 中最强大的特性之一,它允许我们根据条件选择类型。
// 基础条件类型
type IsString<T> = T extends string ? true : false
type Test1 = IsString<string> // true
type Test2 = IsString<number> // false
// 实用的条件类型示例
type NonNullable<T> = T extends null | undefined ? never : T
type ApiResponse<T> = T extends string
? { message: T }
: T extends number
? { code: T }
: { data: T }
type StringResponse = ApiResponse<string> // { message: string }
type NumberResponse = ApiResponse<number> // { code: number }
type ObjectResponse = ApiResponse<{ id: number }> // { data: { id: number } }
2. 映射类型(Mapped Types)
映射类型允许我们基于现有类型创建新类型。
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Partial<T> = {
[P in keyof T]?: T[P]
}
// 高级映射类型示例
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface User {
name: string
age: number
email: string
}
type UserGetters = Getters<User>
// {
// getName: () => string;
// getAge: () => number;
// getEmail: () => string;
// }
// 深度只读类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}
3. 模板字面量类型(Template Literal Types)
模板字面量类型让我们可以在类型层面操作字符串。
// 基础模板字面量类型
type EventName<T extends string> = `on${Capitalize<T>}`
type ClickEvent = EventName<'click'> // 'onClick'
type HoverEvent = EventName<'hover'> // 'onHover'
// 复杂的模板字面量类型
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type Endpoint = '/users' | '/posts' | '/comments'
type APIRoute = `${HTTPMethod} ${Endpoint}`
// 'GET /users' | 'GET /posts' | 'GET /comments' |
// 'POST /users' | 'POST /posts' | 'POST /comments' | ...
// 路径参数提取
type ExtractParams<T extends string> = T extends `${infer _Start}:${infer Param}/${infer Rest}`
? Param | ExtractParams<`/${Rest}`>
: T extends `${infer _Start}:${infer Param}`
? Param
: never
type Params = ExtractParams<'/users/:id/posts/:postId'> // 'id' | 'postId'
实用工具类型
1. 类型安全的事件处理
// 类型安全的事件系统
type EventMap = {
'user:login': { userId: string; timestamp: number }
'user:logout': { userId: string }
'post:create': { postId: string; title: string }
'post:delete': { 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) {
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event]!.push(listener)
}
emit<K extends keyof T>(event: K, data: T[K]) {
const eventListeners = this.listeners[event]
if (eventListeners) {
eventListeners.forEach((listener) => listener(data))
}
}
}
const emitter = new TypedEventEmitter<EventMap>()
// 类型安全的事件监听
emitter.on('user:login', (data) => {
console.log(`用户 ${data.userId} 在 ${data.timestamp} 登录`)
})
// 类型安全的事件发射
emitter.emit('user:login', {
userId: '123',
timestamp: Date.now(),
})
2. 类型安全的 API 客户端
// API 路由定义
type APIRoutes = {
'GET /users': {
response: { users: User[] }
}
'GET /users/:id': {
params: { id: string }
response: { user: User }
}
'POST /users': {
body: { name: string; email: string }
response: { user: User }
}
'PUT /users/:id': {
params: { id: string }
body: Partial<User>
response: { user: User }
}
}
// 提取路由信息的工具类型
type ExtractRouteInfo<T extends keyof APIRoutes> = APIRoutes[T]
type HasParams<T> = T extends { params: any } ? T['params'] : never
type HasBody<T> = T extends { body: any } ? T['body'] : never
type GetResponse<T> = T extends { response: any } ? T['response'] : never
// 类型安全的 API 客户端
class APIClient {
async request<T extends keyof APIRoutes>(
route: T,
...args: HasParams<APIRoutes[T]> extends never
? HasBody<APIRoutes[T]> extends never
? []
: [{ body: HasBody<APIRoutes[T]> }]
: HasBody<APIRoutes[T]> extends never
? [{ params: HasParams<APIRoutes[T]> }]
: [{ params: HasParams<APIRoutes[T]>; body: HasBody<APIRoutes[T]> }]
): Promise<GetResponse<APIRoutes[T]>> {
// 实际的 HTTP 请求逻辑
return {} as GetResponse<APIRoutes[T]>
}
}
const api = new APIClient()
// 类型安全的 API 调用
const users = await api.request('GET /users')
const user = await api.request('GET /users/:id', { params: { id: '123' } })
const newUser = await api.request('POST /users', {
body: { name: 'John', email: 'john@example.com' },
})
3. 状态机类型
// 状态机类型定义
type StateMachine<States extends string, Events extends string> = {
[S in States]: {
[E in Events]?: States
}
}
// 订单状态机
type OrderState = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled'
type OrderEvent = 'process' | 'ship' | 'deliver' | 'cancel'
const orderStateMachine: StateMachine<OrderState, OrderEvent> = {
pending: {
process: 'processing',
cancel: 'cancelled',
},
processing: {
ship: 'shipped',
cancel: 'cancelled',
},
shipped: {
deliver: 'delivered',
},
delivered: {},
cancelled: {},
}
// 类型安全的状态转换
class OrderStateMachine {
constructor(private state: OrderState = 'pending') {}
transition(event: OrderEvent): boolean {
const nextState = orderStateMachine[this.state][event]
if (nextState) {
this.state = nextState
return true
}
return false
}
getState(): OrderState {
return this.state
}
}
类型体操实战
1. 深度合并类型
type DeepMerge<T, U> = {
[K in keyof T | keyof U]: K extends keyof U
? K extends keyof T
? T[K] extends object
? U[K] extends object
? DeepMerge<T[K], U[K]>
: U[K]
: U[K]
: U[K]
: K extends keyof T
? T[K]
: never
}
type Config1 = {
api: {
baseUrl: string
timeout: number
}
features: {
darkMode: boolean
}
}
type Config2 = {
api: {
timeout: number
retries: number
}
features: {
notifications: boolean
}
}
type MergedConfig = DeepMerge<Config1, Config2>
// {
// api: {
// baseUrl: string;
// timeout: number;
// retries: number;
// };
// features: {
// darkMode: boolean;
// notifications: boolean;
// };
// }
2. 函数重载类型
// 函数重载类型定义
type Overload<T extends (...args: any[]) => any> = T extends {
(...args: infer A1): infer R1
(...args: infer A2): infer R2
(...args: infer A3): infer R3
}
? {
(...args: A1): R1
(...args: A2): R2
(...args: A3): R3
}
: T extends {
(...args: infer A1): infer R1
(...args: infer A2): infer R2
}
? {
(...args: A1): R1
(...args: A2): R2
}
: T
// 使用示例
function createElement(tag: 'div'): HTMLDivElement
function createElement(tag: 'span'): HTMLSpanElement
function createElement(tag: 'input'): HTMLInputElement
function createElement(tag: string): HTMLElement {
return document.createElement(tag)
}
type CreateElement = typeof createElement
// 自动推断出所有重载签名
最佳实践
1. 类型守卫
// 类型守卫函数
function isString(value: unknown): value is string {
return typeof value === 'string'
}
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'name' in obj &&
'email' in obj &&
typeof (obj as any).name === 'string' &&
typeof (obj as any).email === 'string'
)
}
// 使用类型守卫
function processData(data: unknown) {
if (isString(data)) {
// data 的类型现在是 string
console.log(data.toUpperCase())
} else if (isUser(data)) {
// data 的类型现在是 User
console.log(`Hello, ${data.name}!`)
}
}
2. 品牌类型
// 品牌类型用于区分相同基础类型的不同用途
type UserId = string & { readonly brand: unique symbol }
type PostId = string & { readonly brand: unique symbol }
function createUserId(id: string): UserId {
return id as UserId
}
function createPostId(id: string): PostId {
return id as PostId
}
function getUser(id: UserId): User {
// 实现获取用户逻辑
return {} as User
}
const userId = createUserId('user-123')
const postId = createPostId('post-456')
getUser(userId) // ✅ 正确
// getUser(postId); // ❌ 类型错误
总结
TypeScript 的高级特性为我们提供了强大的类型安全保障:
- 条件类型:根据条件选择类型
- 映射类型:基于现有类型创建新类型
- 模板字面量类型:类型层面的字符串操作
- 工具类型:提高开发效率和类型安全
- 类型体操:解决复杂的类型推导问题
掌握这些技巧,你就能充分发挥 TypeScript 的威力,写出更安全、更优雅的代码!
TypeScript 的类型系统非常强大,值得我们深入学习和实践。希望这篇文章对你有所帮助!