(下面我使用的是 TypeScript 3.2)
如果我理解的话,您的问题的主要问题是难以在运行时选择正确的重载。将类型信息从 TypeScript 编译成 JavaScript 是 not one of TypeScript's goals(参见非目标 #5)。 TypeScript 添加的类型系统在运行时被完全擦除。因此,如果您想编写compose() 来获取函数列表,则必须以某种方式在运行时检查这些函数以确定应该在特定参数上调用哪个函数。但是,该功能在 JavaScript 中确实不存在。好吧,您可以使用函数的 length 属性来查看它需要多少 许多 个参数,但是在您给出的示例中,每个函数都只接受一个参数。所以我们不能在这里使用这种方法。
一种可能的前进方式是向每个函数添加property。这个属性将是一个方法,它接受一组潜在的参数,如果这些参数对函数有效,则返回true,否则返回false。本质上,您是在手动添加语言中缺少的必要检查能力。
如果我们这样做,我们可以让compose() 接受这样的“参数验证函数”列表,如下所示:
type ArgValidatingFunction =
((...args: any[]) => any) & { validArgs(...args: any): boolean };
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends
((k: infer I) => void) ? I : never;
function compose<F extends ArgValidatingFunction[]>(...fn: F): UnionToIntersection<F[number]>;
function compose(...fn: ArgValidatingFunction[]): Function {
return Object.assign(
(...args: any[]) => (fn.find(f => f.validArgs(...args))!(...args)),
{ validArgs: (...args: any[]) => fn.some(f => f.validArgs(...args)) }
);
}
compose 的类型签名接受ArgValidatingFunction 参数列表并返回intersection of its elements。 TypeScript 将overloads 表示为签名的顺序相关交集。我不能 100% 保证编译器会产生与传入的函数相同的重载顺序,但它似乎在我的测试中有效。
compose 的实现利用了ArgValidatingFunction 的validArgs 方法,并对传入的函数进行find() 以选择合适的函数。我还在返回的函数上实现了validArgs() 方法,以便compose() 的返回值也是ArgValidatingFunction(这很好,因为类型签名声称它是)。
现在我们可以尝试使用它,但这并不简单……我们必须添加那些方法:
const operationA = ({ a, b, c }: { a: any, b: any, c: any }): 'alpha' => 'alpha';
operationA.validArgs = (...args: any[]) =>
(args.length === 1) && ('a' in args[0]) && ('b' in args[0]) && ('c' in args[0]);
const operationB = ({ a, b }: { a: any, b: any }): 'beta' => 'beta';
operationB.validArgs = (...args: any[]) =>
(args.length === 1) && ('a' in args[0]) && ('b' in args[0]);
const operationC = ({ a }: { a: any }): 'gamma' => 'gamma';
operationC.validArgs = (...args: any[]) =>
(args.length === 1) && ('a' in args[0]);
我们开始吧:
const operation = compose(operationA, operationB, operationC);
const beta = operation({ a: 3, b: 3 }); // "beta" at compile time;
console.log(beta); // "beta" at runtime
看起来它在编译时和运行时都有效。
所以这是一种方法。这并不容易或漂亮,但也许它适用于您(或某人)的用例。希望有帮助。祝你好运!