admin管理员组文章数量:1201362
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:
Type alias 'Chain' circularly references itself.
(understand why, don't know how to resole)A rest element type must be an array type.
(probably spread is not available for generic tuples)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
)
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:
Type alias 'Chain' circularly references itself.
(understand why, don't know how to resole)A rest element type must be an array type.
(probably spread is not available for generic tuples)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
)
- Isn't this a "turtles all the way down" problem? i.e. it is impossible to resolve the recursive type. – Fenton Commented Nov 6, 2018 at 15:14
1 Answer
Reset to default 31Circular type aliases are not really supported except in certain cases. (UPDATE TS 4.1, these are more supported now, but I'm still inclined to represent flow()
as operating on AsChain
that verifies a particular array of functions instead of trying to come up with a Chain
that matches all valid arrays of functions)
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 extends [any, ...infer R] ? R : 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 Last<T extends any[]> = T extends [...infer F, infer L] ? L : never;
type LaxReturnType<F> = F extends (...args: any) => infer R ? R : never;
declare function flow<F extends [(arg: any) => any, ...Array<(arg: any) => any>]>(
...f: F & AsChain<F>
): (arg: ArgType<F[0]>) => LaxReturnType<Last<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 returnT[K]
if it can, otherwise it returnsElse
. SoLookup<{a: string}, "a", number>
isstring
, andLookup<{a: string}, "b", number>
isnumber
.Tail<T>
takes a tuple typeT
and returns a tuple with the first element removed. SoTail<["a","b","c"]>
is["b","c"]
.Func1
is just the type of a one-argument function.ArgType<F, Else>
returns the argument type ofF
if it's a one-argument function, andElse
otherwise. SoArgType<(x: string)=>number, boolean>
isstring
, andArgType<123, boolean>
isboolean
.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 inF
with the argument type of the next function (and usingany
for the last one). IfAsChain<F>
is compatible withF
, everything's good. IfAsChain<F>
is incompatible withF
, thenF
is not a good chain. So,AsChain<[(x: string)=>number, (y:number)=>boolean]>
is[(x: string)=>number, (y: number)=>any]
, which is good. ButAsChain<[(x: string)=>number, (y: string)=>boolean]>
is[(x: string)=>string, (y: string)=>any]
, which is not good.Last<T>
takes a tuple and returns the last element, which we need to represent the return type offlow()
.Last<["a","b","c"]>
is"c"
.Finally,
LaxReturnType<F>
is just likeReturnType<F>
but without a constraint onF
.
Okay, hope that helps; good luck!
Playground link to code
本文标签: javascriptTypescript recursive function compositionStack Overflow
版权声明:本文标题:javascript - Typescript recursive function composition - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738573557a2100742.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论