打字稿递归函数组成 [英] Typescript recursive function composition

查看:95
本文介绍了打字稿递归函数组成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个函数链,它将是一个管道/流/组合函数的输入。



这是否可能没有类型的字面扩展选择的深度,这通常处理? 查看lodash的流程



我想实现链中数据流的类型检查。
- 函数的参数是前一个
的结果 - 第一个参数是模板参数
- 最后一个返回是模板参数



< pre class =lang-typescript prettyprint-override> type Chain< In,Out,Tmp1 = any,Tmp2 = any> = [] | [(arg:In)=>出] | [(arg:In)=> Tmp1,(i:Tmp1)=> Tmp2,......链< Tmp2,Out>];

这个想法在草案中。



然而,这会产生以下错误:


  1. 类型别名'Chain'循环引用自身。(了解原因,不知道如何解决)

  2. 休息元素类型必须是数组类型。(可能传播不适用于通用元组)

  3. 类型'链'不是通用的。(甚至不理解为什么这个错误是甚至在这里)

在Typescript中可以定义 Chain 吗?如果是这样,请附上一个片段。



(测试最新的tsc 3.1.6

解决方案

除某些情况外,实际上不支持循环类别别名。我没有尝试用TypeScript友好的方式来表示你在那里写的具体类型,而是我会将你的问题备份并解释为:我们如何键入 flow() -like函数,它将可变数量的单参数函数作为其参数,其中每个单参数函数返回类型是下一个参数函数的参数类型,如链...并且它返回一个表示折叠链的单参数函数?



我有一些我相信有用的东西,但它很复杂,使用了很多条件类型元组传播映射元组。这是:

 类型查找< T,K扩展keyof any,Else = never> = K扩展了T的关键? T [K]:Else 
类型Tail< T扩展任何[]> =
((... t:T)=> void)extends((x:any,... u:推理U)=> void)?你永远不会
类型Func1 =(arg:any)=>任何;
类型ArgType< F,Else = never> = F extends(arg:推断A)=>任何 ?答:其他;
类型AsChain< F extends [Func1,... Func1 []],G扩展Func1 [] = Tail< F>> =
{[keyof F中的K] :( arg:ArgType< F [K]>)=> ArgType< Lookup< G,K,any>,any> };
类型LastIndexOf< T扩展任何[]> =
((... x:T)=> void)extends((y:any,... z:推断U)=> void)
? U ['length']:从不

声明函数流< F extends [(arg:any)=> any,... Array<(arg:any)=>任何>]>(
... f:F& AsChain< F>
):( arg:ArgType< F [0]>)=>返回类型< F [LastIndexOf< f是氟烃基;]取代;

让我们看看它是否有效:

  const stringToString = flow(
(x:string)=> x.length,
(y:number)=> y +!
); //好吧
const str = stringToString(嘿); //它是一个字符串

const tooFewParams = flow(); //错误

const badChain = flow(
(x:number)=>string,
(y:string)=> false,
(z:number)=>oops
); //错误,布尔值不能分配给数字

对我来说很好看。






我不确定是否值得详细了解类型定义的工作方式,但我也可以解释如何使用它们:




  • 查找< T,K,Else> 尝试返回 T [K] 如果可以,否则返回 Else 。所以查找< {a:string},a,数字> 字符串查询< {a:string},b,数字> 数字


  • 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中每个函数的返回类型将其转换为链使用下一个函数的参数类型(并使用任何表示最后一个函数)。如果 AsChain< F> F 兼容,一切都很好。如果 AsChain< F> F 不兼容,则 F 不是一个好链。所以, AsChain< [(x:string)=>数字,(y:数字)=>布尔值]> [(x :string)=> number,(y:number)=> any] ,这很好。但是 AsChain< [(x:string)=>数字,(y:字符串)=>布尔值]> [(x: string)=> string,(y:string)=> any] ,这不好。


  • 最后, LastIndexOf< T> 获取一个元组并返回最后一个索引,我们需要表示返回类型 flow() LastIndexOf< [a,b,c]> 2







好的,希望有所帮助;祝你好运!


I want to create a function chain, which would be an input of a pipe/flow/compose function.

Is this possible without the literal expansion of the types to selected depth, as is this usually handled? See lodash's flow.

I want to achieve typecheck of the data flow in the chain. - Argument of a function is result of the previous one - First argument is a template parameter - Last return is a template parameter

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

The idea is in the draft.

This however produces tho following errors:

  1. Type alias 'Chain' circularly references itself. (understand why, don't know how to resole)
  2. A rest element type must be an array type. (probably spread is not available for generic tuples)
  3. Type 'Chain' is not generic. (don't even understand why this error is even here)

Is this definition of Chain possible in Typescript? If so, please enclose a snippet.

(Tested on latest tsc 3.1.6)

解决方案

Circular type aliases are not really supported except in certain cases. Instead of trying to represent the specific type you've written there in a TypeScript-friendly way, I think I'll back up and interpret your question as: how can we type a flow()-like function, which takes as its arguments a variable number of one-argument functions, where each one-argument-function return type is the argument type for the next one-argument-function, like a chain... and which returns a one-argument function representing the collapsed chain?

I've got something that I believe works, but it's quite complicated, using a lot of conditional types, tuple spreads, and mapped tuples. Here it is:

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>]>;

Let's see if it works:

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

Looks good to me.


I'm not sure if it's worth it to go through in painstaking detail about how the type definitions work, but I might as well explain how to use them:

  • Lookup<T, K, Else> tries to return T[K] if it can, otherwise it returns Else. So Lookup<{a: string}, "a", number> is string, and Lookup<{a: string}, "b", number> is number.

  • Tail<T> takes a tuple type T and returns a tuple with the first element removed. So Tail<["a","b","c"]> is ["b","c"].

  • Func1 is just the type of a one-argument function.

  • ArgType<F, Else> returns the argument type of F if it's a one-argument function, and Else otherwise. So ArgType<(x: string)=>number, boolean> is string, and ArgType<123, boolean> is boolean.

  • AsChain<F> takes a tuple of one-argument functions and tries to turn it into a chain, by replacing the return type of each function in F with the argument type of the next function (and using any for the last one). If AsChain<F> is compatible with F, everything's good. If AsChain<F> is incompatible with F, then F is not a good chain. So, AsChain<[(x: string)=>number, (y:number)=>boolean]> is [(x: string)=>number, (y: number)=>any], which is good. But AsChain<[(x: string)=>number, (y: string)=>boolean]> is [(x: string)=>string, (y: string)=>any], which is not good.

  • Finally, LastIndexOf<T> takes a tuple and returns the last index, which we need to represent the return type of flow(). LastIndexOf<["a","b","c"]> is 2.


Okay, hope that helps; good luck!

这篇关于打字稿递归函数组成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆