Большое предостережение заключается в том, что почти нет шансов, что компилятор TypeScript сможет выводить типы так, как вы этого хотите; вам, вероятно, нередко придется вручную задавать параметры типа или даже утверждать , что конкретная функция является обобщенной c. TypeScript не Haskell, и он не пытается (много).
При этом, вот один из возможных вариантов ввода для variadic
:
interface Variadic<T, U> {
(x: T): Variadic<T, U>
runVariadic: U,
}
const variadic = <T, U>(f: (args: T[]) => U) => {
const go = (args: T[]): Variadic<T, U> =>
Object.defineProperty(
(arg: T) => go(args.concat([arg])),
"runVariadic",
{ get: function () { return f(args) }, enumerable: true });
return go([]);
}
Идея в том, что variadic
принимает функцию, принимающую массив T
и возвращающую U
, и превращает ее в Variadic<T, U>
. Variadic<T, U>
- это функция, которая принимает аргумент T
и возвращает Variadic<T, U>
, а также имеет свойство runVariadic
типа U
.
Вот краткий тест:
const str = variadic((args: string[]) => args)("hey")("you")("guys").runVariadic; // string[]
console.log(str) // ["hey", "you", "guys"]
Здесь я передаю variadic
функцию id
, которая аннотирована для получения и возврата массива строк. Затем полученный Variadic<string, string[]>
может принимать любое количество string
аргументов один за другим, и, наконец, его свойство runVariadic
выводится компилятором как string[]
, что подтверждается журналом консоли.
Для вашего тестового кода необходимо много ручного ввода и подтверждения:
const arrFold = <T, U>(alg: (x: T) => (y: U) => T) => (zero: T) => (xs: U[]) =>
xs.reduce((acc, x) => alg(acc)(x), zero);
const comp = <T, U>(f: (x: T) => U) => <V>(g: (x: V) => T) => (x: V) => f(g(x));
const id = <T>(x: T) => x;
const varComp = variadic(arrFold(comp)(id)) as
Variadic<(x: number) => number, (x: number) => number>;
const inc = (x: number) => x + 1;
const main = varComp(inc)(inc)(inc)(inc)(inc);
console.log(
main.runVariadic(0)); // 5
Ввод arrFold
, comp
и id
достаточно прост, но результирующий тип varComp
, выведенный компилятором, пронизан unknown
s. Вместо этого я утверждал, что это Variadic<(x: number) => number, (x: number) => number>
, поскольку я знаю, что мы передадим ему inc
. Так что main.runVariadic
выводится как (x: number) => number)
, что тоже выглядит неплохо.
Хорошо, надеюсь, это даст вам некоторое направление. Удачи!
Детская площадка ссылка на код