如何定义带参数约束的函数列表类型MatchingFunc?
TypeScript: 约束函数列表的参数兼容(后续函数包含前序参数)
要实现后续函数参数必须包含前序参数的约束,我们需要通过递归类型检查来确保每个函数的参数是前序函数参数的兼容前缀扩展——即后续函数的参数顺序与前序一致,对应位置类型兼容,且可以添加更多参数。以下是完整实现:
// 基础函数类型定义 type Func<P extends any[] = any[], R = any> = (...p: P) => R; type Tail<L extends any[]> = L extends [any, ...infer R] ? R : never; // 检查Curr元组是否以Prev元组为兼容前缀: // 1. Prev为空则直接兼容 // 2. 递归检查每个对应位置的类型,Curr的元素必须能赋值给Prev的对应元素 type IsPrefix<Prev extends any[], Curr extends any[]> = Prev extends [] ? unknown : Curr extends [infer CFirst, ...infer CRest] ? CFirst extends Prev[0] ? IsPrefix<Tail<Prev>, CRest> : never : never; // 递归检查函数列表是否符合要求: // - 函数数量与返回类型列表长度一致 // - 每个函数的返回类型匹配对应位置的指定类型 // - 后续函数参数是前序参数的兼容前缀扩展 type CheckFuncList<FF extends Func[], Rets extends any[], PrevP extends any[] = []> = FF extends [] ? (Rets extends [] ? unknown : never) : Rets extends [] ? never : FF extends [infer FFirst extends Func, ...infer FRest extends Func[]] ? Rets extends [infer RFirst, ...infer RRest extends any[]] ? FFirst extends Func<infer FP, RFirst> ? IsPrefix<PrevP, FP> extends unknown ? CheckFuncList<FRest, RRest, FP> : never : never : never : never; // 最终的MatchingFunc类型:仅返回符合约束的函数列表 type MatchingFunc<Rets extends any[]> = FF extends Func[] ? CheckFuncList<FF, Rets> extends unknown ? FF : never : never; // 声明use函数,约束传入的函数列表必须符合MatchingFunc declare function use<Rets extends any[], FF extends MatchingFunc<Rets>>(...ff: FF): any;
代码说明
- IsPrefix类型:核心约束逻辑,递归验证后续函数的参数是否包含前序参数的兼容前缀。如果所有对应位置的类型都兼容(
CFirst extends Prev[0]),则返回unknown表示兼容;否则返回never表示不兼容。 - CheckFuncList类型:递归遍历函数列表和返回类型列表,同时跟踪前序函数的参数类型,确保每个函数的返回类型正确且参数符合前缀兼容要求。
- MatchingFunc类型:作为对外暴露的约束类型,仅当函数列表完全符合要求时才返回该列表类型,否则返回
never,触发TypeScript报错。
测试验证
// 错误示例:第二个函数参数类型与前序不兼容,会触发报错 const test = use( (a: string, b: number) => a, (a: number, b: string) => !a, // ❌ 类型不兼容 ); // 正确示例:第二个函数参数包含前序参数,类型兼容 const test2 = use( (a: string, b: number) => a, (a: string, b: number, c: boolean) => !c, // ✅ 参数扩展且兼容 ); // 正确示例:参数与前序完全一致 const test3 = use( (a: string) => a, (a: string) => true, // ✅ 参数完全匹配 ); // 错误示例:参数少于前序,触发报错 const test4 = use( (a: string, b: number) => a, (a: string) => true, // ❌ 参数数量不足 );
内容的提问来源于stack exchange,提问作者zedryas
相关产品推荐
相关产品推荐

