类型编程最佳实践
类型编程是 TypeScript 的核心能力之一,通过合理运用类型系统,可以显著提升代码的健壮性和可维护性。从基础类型操作到高级模式匹配,类型编程的实践方式多种多样,关键在于理解类型系统的底层逻辑和实际应用场景。
基础类型操作与工具类型
TypeScript 内置了大量工具类型,合理使用它们能减少重复代码。例如 Partial<T>
可以将所有属性变为可选:
interface User {
id: number
name: string
}
type PartialUser = Partial<User>
// 等价于 { id?: number; name?: string }
Pick
和 Omit
是处理对象类型的利器:
type UserName = Pick<User, 'name'> // { name: string }
type UserWithoutId = Omit<User, 'id'> // { name: string }
自定义工具类型时,条件类型是基础构建块:
type Nullable<T> = T | null
type ValueOf<T> = T[keyof T]
类型推断与模式匹配
infer
关键字在解构复杂类型时非常有用。例如提取函数返回类型:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
function getUser() { return { id: 1 } }
type UserReturn = ReturnType<typeof getUser> // { id: number }
处理数组类型时,递归类型表现出色:
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T
type Nested = number[][][]
type Flat = Flatten<Nested> // number
模板字面量类型实战
4.1 版本引入的模板字面量类型可以创建精细的字符串约束:
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type ApiPath = `/${string}`
type Endpoint = `${HttpMethod} ${ApiPath}`
const endpoint: Endpoint = 'GET /users' // 合法
结合映射类型可以实现动态属性生成:
type EventMap = {
click: MouseEvent
focus: FocusEvent
}
type HandlerMap = {
[K in keyof EventMap as `on${Capitalize<K>}`]: (event: EventMap[K]) => void
}
// 生成 { onClick: (event: MouseEvent) => void; onFocus: (event: FocusEvent) => void }
类型守卫与可辨识联合
自定义类型守卫能显著改善类型窄化:
function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every(item => typeof item === 'string')
}
const data: unknown = ['a', 'b']
if (isStringArray(data)) {
data.map(s => s.toUpperCase()) // 安全调用
}
可辨识联合(Discriminated Unions)是处理复杂状态的最佳实践:
type Result<T> =
| { status: 'success'; data: T }
| { status: 'error'; message: string }
function handleResult(result: Result<number>) {
switch (result.status) {
case 'success':
console.log(result.data * 2) // 自动窄化
break
case 'error':
console.error(result.message)
break
}
}
高级模式:类型级编程
实现类型安全的 Curry 函数需要深度类型操作:
type Curry<F> = F extends (...args: infer A) => infer R
? A extends [infer First, ...infer Rest]
? (arg: First) => Curry<(...args: Rest) => R>
: R
: never
function curry<F>(fn: F): Curry<F> {
// 实现省略
}
const add = (a: number, b: number) => a + b
const curriedAdd = curry(add)
const add5 = curriedAdd(5) // (b: number) => number
类型谓词可以创建复杂的验证逻辑:
type IPv4 = `${number}.${number}.${number}.${number}`
function isIPv4(str: string): str is IPv4 {
const parts = str.split('.')
return parts.length === 4 && parts.every(part => {
const num = parseInt(part, 10)
return num >= 0 && num <= 255 && part === num.toString()
})
}
性能优化与类型缓存
复杂类型运算可能导致编译器性能下降,这时需要类型缓存:
type DeepReadonly<T> =
T extends Function ? T :
T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } :
T
// 使用接口缓存中间类型
interface ReadonlyUser extends DeepReadonly<User> {}
避免过度使用递归类型,必要时设置深度限制:
type DeepPartial<T, Depth extends number = 3> =
[Depth] extends [0] ? T :
T extends object ? { [K in keyof T]?: DeepPartial<T[K], [-1, 0, 1, 2][Depth]> } :
T
类型与运行时值的同步
通过 const assertions
保持类型与值同步:
const routes = ['home', 'users', 'settings'] as const
type Route = typeof routes[number] // "home" | "users" | "settings"
创建类型安全的枚举替代方案:
function createEnum<T extends string>(...values: T[]) {
return Object.freeze(values.reduce((acc, val) => {
acc[val] = val
return acc
}, {} as { [K in T]: K }))
}
const Direction = createEnum('North', 'South', 'East', 'West')
type Direction = keyof typeof Direction // "North" | "South" | "East" | "West"
类型系统边界处理
处理动态数据结构时需要灵活的类型设计:
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue }
function safeParse(json: string): unknown {
try {
return JSON.parse(json)
} catch {
return undefined
}
}
const data = safeParse('{"a":1}')
if (data && typeof data === 'object' && 'a' in data) {
// 类型窄化处理
}
处理第三方库类型扩展时,模块增强是标准做法:
declare module 'some-library' {
interface Config {
customOption?: boolean
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn