推断函数参数类型并在 Typescript 中传播它们
Infer function parameter types and spread them in Typescript
给定一个函数类型 F,我想创建一个新类型 "composable before" F,这意味着它接受和 returns F 接受的相同参数。例如:
const stringToNumber = (s: string) => s.length;
const stringToString: ComposableOn<(s: string) => number> = (s: string) => s + s;
const composedResult = stringToNumber(stringToString('a'));
不过,我无法正确定义类型 ComposableOn
。以下是我尝试过的事情:
type ComposableBefore<F extends (...args: any) => any> = (args: Parameters<F>) => Parameters<F>;
type StoN = (s: string) => number;
const sToS: ComposableBefore<StoN> = (s: string) => s + s; // error: the type is [string] => [string] and not string => string
type ComposableBefore<F extends (...args: any) => any> = Parameters<F> extends Array<infer U>? (...args: Parameters<F>) => U: never;
const complex: ComposableBefore<(a: string, b: number, c: { d: number, e: string }) => number> = (a, b, c) => c; // not good either, since it can return a value of any type of the original function's argument types.
正确的输入方式是什么?
您可以使用...
直接在函数签名中传播Parameters
。至于return,因为你想支持多个参数,return类型应该是一个元组,因此你需要传播你的函数的return:
const stringToNumber = (s: string) => s.length;
const stringToString: ComposableBefore<(s: string) => number> = (s: string) => [s + s];
const composedResult = stringToNumber(...stringToString('a'));
type ComposableBefore<F extends (...args: any) => any> = (...args: Parameters<F>) => Parameters<F>;
type StoN = (s: string) => number;
您还可以考虑为单参数函数添加一个特殊情况,如果这是常见的用例:
const stringToNumber = (s: string) => s.length;
const stringToString: ComposableBefore<(s: string) => number> = (s: string) => s + s;
const composedResult = stringToNumber(stringToString('a'));
const multiStringToNumber = (s: string, s2: string) => s.length + s2.length;
const multiStringToString: ComposableBefore<typeof multiStringToNumber> = (s: string, s2: string) => [s + s, s2 + s2];
const multiComposedResult = multiStringToNumber(...multiStringToString('a', 'b'));
type ComposableBefore<F extends (...args: any) => any> =
F extends (a: infer U) => any ? (...args: Parameters<F>) => U :
(...args: Parameters<F>) => Parameters<F>;
给定一个函数类型 F,我想创建一个新类型 "composable before" F,这意味着它接受和 returns F 接受的相同参数。例如:
const stringToNumber = (s: string) => s.length;
const stringToString: ComposableOn<(s: string) => number> = (s: string) => s + s;
const composedResult = stringToNumber(stringToString('a'));
不过,我无法正确定义类型 ComposableOn
。以下是我尝试过的事情:
type ComposableBefore<F extends (...args: any) => any> = (args: Parameters<F>) => Parameters<F>;
type StoN = (s: string) => number;
const sToS: ComposableBefore<StoN> = (s: string) => s + s; // error: the type is [string] => [string] and not string => string
type ComposableBefore<F extends (...args: any) => any> = Parameters<F> extends Array<infer U>? (...args: Parameters<F>) => U: never;
const complex: ComposableBefore<(a: string, b: number, c: { d: number, e: string }) => number> = (a, b, c) => c; // not good either, since it can return a value of any type of the original function's argument types.
正确的输入方式是什么?
您可以使用...
直接在函数签名中传播Parameters
。至于return,因为你想支持多个参数,return类型应该是一个元组,因此你需要传播你的函数的return:
const stringToNumber = (s: string) => s.length;
const stringToString: ComposableBefore<(s: string) => number> = (s: string) => [s + s];
const composedResult = stringToNumber(...stringToString('a'));
type ComposableBefore<F extends (...args: any) => any> = (...args: Parameters<F>) => Parameters<F>;
type StoN = (s: string) => number;
您还可以考虑为单参数函数添加一个特殊情况,如果这是常见的用例:
const stringToNumber = (s: string) => s.length;
const stringToString: ComposableBefore<(s: string) => number> = (s: string) => s + s;
const composedResult = stringToNumber(stringToString('a'));
const multiStringToNumber = (s: string, s2: string) => s.length + s2.length;
const multiStringToString: ComposableBefore<typeof multiStringToNumber> = (s: string, s2: string) => [s + s, s2 + s2];
const multiComposedResult = multiStringToNumber(...multiStringToString('a', 'b'));
type ComposableBefore<F extends (...args: any) => any> =
F extends (a: infer U) => any ? (...args: Parameters<F>) => U :
(...args: Parameters<F>) => Parameters<F>;