4.0 KiB
4.0 KiB
类型模式
工具类型
各库中常用的辅助类型:
// Promise 或同步
export type Awaitable<T> = T | Promise<T>
// 单个或数组
export type Arrayable<T> = T | T[]
// 可空
export type Nullable<T> = T | null | undefined
// 深度可选
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}
// 简化交叉类型以改善 IDE 显示
export type Simplify<T> = { [K in keyof T]: T[K] } & {}
// 阻止特定位置的类型推断
export type NoInfer<T> = [T][T extends any ? 0 : never]
条件提取
从结构中提取类型:
// 从 schema 中提取输入类型
export type Input<T> = T extends { _input: infer U } ? U : unknown
// 提取输出类型
export type Output<T> = T extends { _output: infer U } ? U : unknown
// 从嵌套属性中提取
export type InferContext<T> = T extends { context: infer C } ? C : never
品牌类型
为原始类型添加名义类型:
declare const brand: unique symbol
export type Brand<T, B> = T & { readonly [brand]: B }
export type UserId = Brand<string, 'UserId'>
export type PostId = Brand<string, 'PostId'>
// 不会混淆
function getUser(id: UserId) { /* ... */ }
getUser('abc' as UserId) // 正确
getUser('abc' as PostId) // 错误!
类型累积(构建器模式)
每个方法更新泛型参数:
interface ProcedureBuilder<TContext, TInput, TOutput> {
input<T>(schema: T): ProcedureBuilder<TContext, T, TOutput>
output<T>(schema: T): ProcedureBuilder<TContext, TInput, T>
query(fn: (opts: { ctx: TContext; input: TInput }) => TOutput): Procedure
}
// 类型沿链式调用传递
const proc = builder
.input(z.object({ id: z.string() })) // TInput = { id: string }
.output(z.object({ name: z.string() })) // TOutput = { name: string }
.query(({ input }) => ({ name: input.id }))
模块扩充
允许用户扩展库类型:
// 库代码
export interface Register {}
export type DefaultError = Register extends { defaultError: infer E }
? E
: Error
// 用户代码
declare module 'my-lib' {
interface Register {
defaultError: MyCustomError
}
}
数据标记
使用 symbol 附加类型元数据:
declare const dataTagSymbol: unique symbol
declare const errorTagSymbol: unique symbol
export type DataTag<TType, TData, TError> = TType & {
[dataTagSymbol]: TData
[errorTagSymbol]: TError
}
// 提取标记类型
export type InferData<T> = T extends { [dataTagSymbol]: infer D } ? D : unknown
映射类型修饰
列构建器模式(drizzle):
type NotNull<T extends ColumnBuilder> = T & { _: { notNull: true } }
type HasDefault<T extends ColumnBuilder> = T & { _: { hasDefault: true } }
class ColumnBuilder<T extends ColumnConfig> {
notNull(): NotNull<this> {
// ...
return this as NotNull<this>
}
default(value: T['data']): HasDefault<this> {
// ...
return this as HasDefault<this>
}
}
编译时错误
返回可读的错误信息:
type TypeError<Message extends string> = { __error: Message }
type ValidateInput<T> = T extends string
? T
: TypeError<'Input must be a string'>
// 显示:类型 "TypeError<"Input must be a string">" 不可赋值给...
函数重载
为不同输入提供多个签名:
export function useEventListener<E extends keyof WindowEventMap>(
event: E,
listener: (ev: WindowEventMap[E]) => any
): void
export function useEventListener<E extends keyof DocumentEventMap>(
target: Document,
event: E,
listener: (ev: DocumentEventMap[E]) => any
): void
export function useEventListener(...args: any[]) {
// 实现
}
分布式条件类型
应用于每个联合成员:
type ToArray<T> = T extends any ? T[] : never
type Result = ToArray<string | number>
// Result = string[] | number[]
使用元组禁用分发:
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type Result = ToArrayNonDist<string | number>
// Result = (string | number)[]