Композиция рекурсивной функции машинописи - PullRequest
0 голосов
/ 06 ноября 2018

Я хочу создать цепочку функций, которая будет входом функции pipe / flow / compose.

Возможно ли это без буквального расширения типов до выбранной глубины, как это обычно обрабатывается? См. Поток Лодаша .

Я хочу добиться проверки типа потока данных в цепочке. - Аргумент функции является результатом предыдущего - Первый аргумент является параметром шаблона - Последнее возвращение является параметром шаблона

type Chain<In, Out, Tmp1 = any, Tmp2 = any> = [] | [(arg: In) => Out] | [(arg: In) => Tmp1, (i: Tmp1) => Tmp2, ...Chain<Tmp2, Out>];

Идея в проекте.

Это, однако, приводит к следующим ошибкам:

  1. Type alias 'Chain' circularly references itself. (понимаю почему, не знаю, как восстановить)
  2. A rest element type must be an array type. (возможно, распространение не доступно для универсальных кортежей)
  3. Type 'Chain' is not generic. (даже не понимаю, почему эта ошибка даже здесь)

Возможно ли это определение Chain в Typescript? Если это так, пожалуйста, приложите фрагмент.

(проверено на последнем tsc 3.1.6)

1 Ответ

0 голосов
/ 06 ноября 2018

Псевдонимы циклического типа не поддерживаются, за исключением некоторых случаев. Вместо того, чтобы пытаться представить конкретный тип, который вы там написали, дружественным к TypeScript, я думаю, что я сделаю резервную копию и интерпретирую ваш вопрос следующим образом: как мы можем напечатать flow() -подобную функцию, которая принимает в качестве аргументов переменное число функций с одним аргументом, где каждый возвращаемый тип функции с одним аргументом является типом аргумента для следующей функции с одним аргументом, как цепочка ... и который возвращает функцию с одним аргументом, представляющую свернутую цепочку?

У меня есть кое-что, что, на мой взгляд, работает, но это довольно сложно, с использованием множества условных типов , спредов кортежей и сопоставленных кортежей . Вот оно:

type Lookup<T, K extends keyof any, Else=never> = K extends keyof T ? T[K] : Else
type Tail<T extends any[]> = 
  ((...t: T) => void) extends ((x: any, ...u: infer U) => void) ? U : never;
type Func1 = (arg: any) => any;
type ArgType<F, Else=never> = F extends (arg: infer A) => any ? A : Else;
type AsChain<F extends [Func1, ...Func1[]], G extends Func1[]= Tail<F>> =
  { [K in keyof F]: (arg: ArgType<F[K]>) => ArgType<Lookup<G, K, any>, any> };
type LastIndexOf<T extends any[]> =
  ((...x: T) => void) extends ((y: any, ...z: infer U) => void)
  ? U['length'] : never

declare function flow<F extends [(arg: any) => any, ...Array<(arg: any) => any>]>(
  ...f: F & AsChain<F>
): (arg: ArgType<F[0]>) => ReturnType<F[LastIndexOf<F>]>;

Посмотрим, работает ли он:

const stringToString = flow(
  (x: string) => x.length, 
  (y: number) => y + "!"
); // okay
const str = stringToString("hey"); // it's a string

const tooFewParams = flow(); // error

const badChain = flow(
  (x: number)=>"string", 
  (y: string)=>false, 
  (z: number)=>"oops"
); // error, boolean not assignable to number

Хорошо выглядит для меня.


Я не уверен, стоит ли подробно рассказывать о том, как работают определения типов, но я мог бы также объяснить, как их использовать:

  • Lookup<T, K, Else> пытается вернуть T[K], если может, в противном случае возвращается Else. Так что Lookup<{a: string}, "a", number> равно string, а Lookup<{a: string}, "b", number> равно number.

  • Tail<T> принимает тип кортежа T и возвращает кортеж с удаленным первым элементом. Так что Tail<["a","b","c"]> - это ["b","c"].

  • Func1 - это просто тип функции с одним аргументом.

  • ArgType<F, Else> возвращает тип аргумента F, если это функция с одним аргументом, и Else в противном случае. Так что ArgType<(x: string)=>number, boolean> равно string, а ArgType<123, boolean> равно boolean.

  • AsChain<F> принимает набор функций с одним аргументом и пытается превратить его в цепочку, заменяя тип возвращаемого значения каждой функции в F на тип аргумента следующей функции (и используя any за последний). Если AsChain<F> совместим с F, все хорошо. Если AsChain<F> несовместимо с F, то F не является хорошей цепочкой. Итак, AsChain<[(x: string)=>number, (y:number)=>boolean]> - это [(x: string)=>number, (y: number)=>any], что хорошо. Но AsChain<[(x: string)=>number, (y: string)=>boolean]> - это [(x: string)=>string, (y: string)=>any], что нехорошо.

  • Наконец, LastIndexOf<T> берет кортеж и возвращает последний индекс, который нам нужен для представления типа возврата flow(). LastIndexOf<["a","b","c"]> is 2.


Хорошо, надеюсь, это поможет; удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...