Этот код взят из потрясающей статьи Представляем Рекурсивные Pipe
и Compose
Типы .
Работает на typescript@3.4.5, но не работает с текущей последней версия 3.8.3.
Для правильной компиляции код должен быть разбит на два файла.
//file Types.ts
export type ExtractFunctionArguments<Fn> = Fn extends (...args: infer P) => any
? P
: never;
export type ExtractFunctionReturnValue<Fn> = Fn extends (
...args: any[]
) => infer P
? P
: never;
type BooleanSwitch<Test, T = true, F = false> = Test extends true ? T : F;
export type AnyFunction = (...args: any[]) => any;
export type AnyFunction1 = (a: any) => any;
type Arbitrary = "It is now 1554792354 seconds since since Jan 01, 1970";
type IsAny<O, T = true, F = false> = Arbitrary extends O
? any extends O
? T
: F
: F;
export type Pipe<
Fns extends any[],
IsPipe = true,
PreviousFunction = void,
InitialParams extends any[] = any[],
ReturnType = any
> = {
next: ((..._: Fns) => any) extends (_: infer First, ..._1: infer Next) => any
? PreviousFunction extends void
? Pipe<
Next,
IsPipe,
First,
ExtractFunctionArguments<First>,
ExtractFunctionReturnValue<First>
>
: ReturnType extends ExtractFunctionArguments<First>[0]
? Pipe<
Next,
IsPipe,
First,
InitialParams,
ExtractFunctionReturnValue<First>
>
: IsAny<ReturnType> extends true
? Pipe<
Next,
IsPipe,
First,
InitialParams,
ExtractFunctionReturnValue<First>
>
: {
ERROR: [
"Return type ",
ReturnType,
"does comply with the input of",
ExtractFunctionArguments<First>[0]
];
POSITION: [
"Position of problem for input arguments is at",
Fns["length"],
"from the",
BooleanSwitch<IsPipe, "end", "beginning">,
"and the output of function to the ",
BooleanSwitch<IsPipe, "left", "right">
];
}
: never;
done: (...args: InitialParams) => ReturnType;
}[Fns extends [] ? "done" : "next"];
export type PipeFn = <Fns extends [AnyFunction, ...AnyFunction1[]]>(
...fns: Fns & Pipe<Fns> extends AnyFunction ? Fns : never
) => Pipe<Fns>;
export type PipelineFn = <
Arg,
Fns extends [(arg: Arg) => any, ...AnyFunction1[]]
>(
arg: Arg,
...fns: Fns & Pipe<Fns> extends AnyFunction ? Fns : never
) => ExtractFunctionReturnValue<Pipe<Fns>> extends {
ERROR: [string];
POSITION: [string];
}
? Pipe<Fns>
: ExtractFunctionReturnValue<Pipe<Fns>>;
//file Scenario.ts
import { PipeFn, AnyFunction, AnyFunction1 } from "./Types";
export const pipe: PipeFn = (entry: AnyFunction, ...funcs: AnyFunction1[]) => (
...arg: unknown[]
) => funcs.reduce((acc, item) => item.call(item, acc), entry(...arg)); // Compile time error in TypeScript@3.8.3
const add = (x: number, y: number) => x + y;
const inc = (x: number) => add(1, x);
const convertNumberToString = (x: NonNullable<number>) => x.toString();
let pipedFunc = pipe(add, inc, convertNumberToString);
Вопросы
- Почему де код не компилируется в текущей версии TypeScript, есть ли обходной путь?
- Есть ли какой-нибудь более безопасный способ обеспечения компоновки функций в TypeScript?
- Любые советы, как отлаживать ошибки такого рода ?
Ошибка компилятора typescript@3.8.3
npx tsc Scenario.ts --noErrorTruncation
Scenario.ts(3,29): error TS2322: Type '(entry: AnyFunction, ...funcs: AnyFunction1[]) => (...arg: unknown[]) => any' is not assignable to type 'PipeFn'.
Type '(...arg: unknown[]) => any' is not assignable to type '{ next: (..._: Fns) => any extends (_: infer First, ..._1: infer Next) => any ? { next: (..._: Next) => any extends (_: infer First, ..._1: infer Next) => any ? First extends void ? any[Next extends [] ? "done" : "next"] : ExtractFunctionReturnValue<First> extends ExtractFunctionArguments<First>[0] ? any[Next extends [] ? "done" : "next"] : IsAny<ExtractFunctionReturnValue<First>, true, false> extends true ? any[Next extends [] ? "done" : "next"] : { ERROR: ["Return type ", ExtractFunctionReturnValue<First>, "does comply with the input of", ExtractFunctionArguments<First>[0]]; POSITION: ["Position of problem for input arguments is at", Next["length"], "from the", "end", "and the output of function to the ", "left"]; } : never; done: (...args: ExtractFunctionArguments<First>) => ExtractFunctionReturnValue<First>; }[Next extends [] ? "done" : "next"] : never; done: (...args: any[]) => any; }[Fns extends [] ? "done" : "next"]'.
Type '(...arg: unknown[]) => any' is not assignable to type '(..._: Fns) => any extends (_: infer First, ..._1: infer Next) => any ? { next: (..._: Next) => any extends (_: infer First, ..._1: infer Next) => any ? First extends void ? any[Next extends [] ? "done" : "next"] : ExtractFunctionReturnValue<First> extends ExtractFunctionArguments<First>[0] ? any[Next extends [] ? "done" : "next"] : IsAny<ExtractFunctionReturnValue<First>, true, false> extends true ? any[Next extends [] ? "done" : "next"] : { ERROR: ["Return type ", ExtractFunctionReturnValue<First>, "does comply with the input of", ExtractFunctionArguments<First>[0]]; POSITION: ["Position of problem for input arguments is at", Next["length"], "from the", "end", "and the output of function to the ", "left"]; } : never; done: (...args: ExtractFunctionArguments<First>) => ExtractFunctionReturnValue<First>; }[Next extends [] ? "done" : "next"] : never'.