TypeScript中是否有更优的管道化Type Guard函数实现方案?
实现方案
通过 TypeScript 递归可变元组约束即可实现任意长度的类型守卫管道,无需手动声明大量泛型参数,天然支持前一个守卫的输出类型自动作为下一个守卫的入参类型。
完整实现代码如下:
// 基础类型守卫定义 type AnyTypeGuard = (value: any, ...args: any[]) => value is any; // 递归约束守卫链的类型,确保上一个守卫的输出是下一个守卫的输入 type GuardChain<PrevType, GuardList extends AnyTypeGuard[]> = GuardList extends [infer FirstGuard, ...infer RestGuards] ? FirstGuard extends (value: PrevType, ...args: any[]) => value is infer NextType ? RestGuards extends AnyTypeGuard[] ? [FirstGuard, ...GuardChain<NextType, RestGuards>] : never : never : []; // 递归合并所有守卫的断言类型,得到最终的交叉类型 type CombinedGuardType<GuardList extends AnyTypeGuard[], Result = unknown> = GuardList extends [infer FirstGuard, ...infer RestGuards] ? FirstGuard extends (value: any, ...args: any[]) => value is infer U ? RestGuards extends AnyTypeGuard[] ? CombinedGuardType<RestGuards, Result & U> : never : never : Result; // 管道函数实现 function guardPipe<T extends AnyTypeGuard[]>( ...guards: GuardChain<unknown, T> ): (value: unknown) => value is CombinedGuardType<T> { return (value: unknown): value is CombinedGuardType<T> => { for (const guard of guards) { if (!guard(value)) return false; } return true; }; }
使用示例
const isFoobar = guardPipe( (val): val is string => typeof val === 'string', (val): val is `foo${string}` => val.startsWith('foo'), // val 自动推导为 string (val): val is 'foobar' => val === 'foobar' // val 自动推导为 `foo${string}` ); const test = {} as unknown; if (isFoobar(test)) { test // 自动推导为字面量类型 'foobar' } // 错误用法会被 TypeScript 自动拦截 // const wrongGuard = guardPipe( // (val): val is number => typeof val === 'number', // (val: string) => val.startsWith('a') // 类型不匹配直接报错 // )
核心逻辑说明
GuardChain从第一个守卫入参为unknown开始递归遍历守卫列表,强制要求每个守卫的入参类型等于上一个守卫的断言输出类型,从类型定义层面保证守卫链的合法性。CombinedGuardType递归收集所有守卫的断言类型做交叉,最终返回的守卫函数会给出最精确的收敛类型。- 运行时逻辑非常简单,遍历所有守卫依次校验,全部通过才返回 true。
内容的提问来源于stack exchange,提问作者Nico
相关产品推荐
相关产品推荐

