如何约束TypeScript的myEndpoint函数接收符合指定类型的扩展函数数组?
改造TypeScript类型定义,限制函数数组的最终返回类型
现有类型定义
type objectExtensionFunction<TBaseObject extends object, TExtendWith extends object> = (base: TBaseObject) => TExtendWith & TBaseObject; declare function myEndpoint<TObjects extends objectExtensionFunction<any, any>[]>(objects: TObjects): void;
其中objectExtensionFunction是接收基础对象、返回扩展后对象的函数。
需求说明
需要修改myEndpoint的类型定义,让它只接收符合以下条件的函数数组:当所有函数以空对象为初始参数链式调用后,最终返回的对象必须继承指定的requiredEndType(比如包含a、b两个布尔属性的类型)。
示例场景
- 传入
[addToBaseA, addToBaseB]:合法,允许调用 - 传入
[addToBaseA, addToBaseB, addToBaseC]:合法,允许调用 - 传入
[addToBaseB, addToBaseA]:合法,允许调用(顺序不影响结果) - 传入
[addToBaseA, addToBaseC]:非法,触发类型错误(缺少b属性)
实现方案
我们可以通过递归计算函数数组链式调用后的最终类型,再和requiredEndType做约束来实现需求,具体代码如下:
// 定义目标最终类型:必须包含a、b布尔属性 type RequiredEndType = { a: boolean; b: boolean }; // 递归计算函数数组链式调用后的结果类型 type ChainResult<Fs extends objectExtensionFunction<any, any>[], Acc extends object = {}> = Fs extends [infer F, ...infer Rest] ? F extends objectExtensionFunction<any, any> ? ChainResult<Rest extends objectExtensionFunction<any, any>[] ? Rest : [], ReturnType<F>> : Acc : Acc; // 改造后的myEndpoint:仅允许最终结果符合RequiredEndType的函数数组 declare function myEndpoint<Fs extends objectExtensionFunction<any, any>[]>( objects: Fs extends any[] ? (ChainResult<Fs> extends RequiredEndType ? Fs : never) : never ): void; // 测试用的扩展函数示例 const addToBaseA = (base: object) => ({ ...base, a: true }); const addToBaseB = (base: object) => ({ ...base, b: true }); const addToBaseC = (base: object) => ({ ...base, c: true }); // 合法调用示例 myEndpoint([addToBaseA, addToBaseB]); // 通过类型检查 myEndpoint([addToBaseA, addToBaseB, addToBaseC]); // 通过类型检查 myEndpoint([addToBaseB, addToBaseA]); // 通过类型检查 // 非法调用示例(会触发类型错误) myEndpoint([addToBaseA, addToBaseC]); // 报错:返回对象缺少b属性
代码说明
ChainResult类型:通过递归遍历函数数组,依次计算每个函数调用后的返回类型,最终得到链式调用后的完整对象类型。myEndpoint的类型约束:利用条件类型判断链式调用结果是否符合RequiredEndType,不符合则将参数类型设为never,直接触发TypeScript的类型错误提示。
内容的提问来源于stack exchange,提问作者Adam Baranyai
相关产品推荐
相关产品推荐

