打字稿递归函数组合
Typescript recursive function composition
我想创建一个函数链,它是 pipe/flow/compose 函数的输入。
如果不将类型字面扩展到选定的深度,这是否可能,这通常是这样处理的? See lodash's flow.
我想实现链上数据流的typecheck。
- 函数的参数是前一个函数的结果
- 第一个参数是模板参数
- 最后 return 是模板参数
type Chain<In, Out, Tmp1 = any, Tmp2 = any> = [] | [(arg: In) => Out] | [(arg: In) => Tmp1, (i: Tmp1) => Tmp2, ...Chain<Tmp2, Out>];
想法在草稿中。
然而,这会产生以下错误:
Type alias 'Chain' circularly references itself.
(明白为什么,不知道怎么resole)
A rest element type must be an array type.
(可能传播不适用于通用元组)
Type 'Chain' is not generic.
(甚至不明白为什么会出现这个错误)
Chain
的这个定义在 Typescript 中是否可行?如果是这样,请附上一个片段。
(在最新的 tsc 3.1.6
上测试)
除非在某些情况下,否则实际上不支持循环类型别名。 (更新 TS 4.1,这些现在得到了更多支持,但我仍然倾向于将 flow()
表示为在 AsChain
上运行,即 验证特定的 函数数组试图想出一个 Chain
来匹配 所有有效的 函数数组)
与其尝试以 TypeScript 友好的方式表示您在那里编写的特定类型,我想我会备份并将您的问题解释为:我们如何键入 flow()
-like函数,它将可变数量的单参数函数作为其参数,其中每个单参数函数 return 类型是下一个单参数函数的参数类型,如链... return是表示折叠链的单参数函数吗?
我有一些我认为有用的东西,但它很复杂,使用了很多 conditional types, tuple spreads, and mapped tuples。这是:
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>>;
让我们看看它是否有效:
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>
会尝试 return T[K]
,否则会 returns Else
。所以Lookup<{a: string}, "a", number>
是string
,Lookup<{a: string}, "b", number>
是number
。
Tail<T>
采用元组类型 T
并且 return 是第一个元素被删除的元组。所以 Tail<["a","b","c"]>
是 ["b","c"]
.
Func1
只是单参数函数的类型。
ArgType<F, Else>
return如果是单参数函数,则为 F
的参数类型,否则为 Else
。所以ArgType<(x: string)=>number, boolean>
是string
,ArgType<123, boolean>
是boolean
。
AsChain<F>
采用单参数函数的元组并尝试通过替换 F
中每个函数的 return 类型将其变成一个链使用下一个函数的参数类型(最后一个函数使用 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]
,这样不好
Last<T>
取一个元组,return是最后一个元素,我们需要用它来表示flow()
的return类型。 Last<["a","b","c"]>
是 "c"
。
最后,LaxReturnType<F>
和 ReturnType<F>
一样,但对 F
.
没有限制
好的,希望对您有所帮助;祝你好运!
我想创建一个函数链,它是 pipe/flow/compose 函数的输入。
如果不将类型字面扩展到选定的深度,这是否可能,这通常是这样处理的? See lodash's flow.
我想实现链上数据流的typecheck。 - 函数的参数是前一个函数的结果 - 第一个参数是模板参数 - 最后 return 是模板参数
type Chain<In, Out, Tmp1 = any, Tmp2 = any> = [] | [(arg: In) => Out] | [(arg: In) => Tmp1, (i: Tmp1) => Tmp2, ...Chain<Tmp2, Out>];
想法在草稿中。
然而,这会产生以下错误:
Type alias 'Chain' circularly references itself.
(明白为什么,不知道怎么resole)A rest element type must be an array type.
(可能传播不适用于通用元组)Type 'Chain' is not generic.
(甚至不明白为什么会出现这个错误)
Chain
的这个定义在 Typescript 中是否可行?如果是这样,请附上一个片段。
(在最新的 tsc 3.1.6
上测试)
除非在某些情况下,否则实际上不支持循环类型别名。 (更新 TS 4.1,这些现在得到了更多支持,但我仍然倾向于将 flow()
表示为在 AsChain
上运行,即 验证特定的 函数数组试图想出一个 Chain
来匹配 所有有效的 函数数组)
与其尝试以 TypeScript 友好的方式表示您在那里编写的特定类型,我想我会备份并将您的问题解释为:我们如何键入 flow()
-like函数,它将可变数量的单参数函数作为其参数,其中每个单参数函数 return 类型是下一个单参数函数的参数类型,如链... return是表示折叠链的单参数函数吗?
我有一些我认为有用的东西,但它很复杂,使用了很多 conditional types, tuple spreads, and mapped tuples。这是:
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>>;
让我们看看它是否有效:
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>
会尝试 returnT[K]
,否则会 returnsElse
。所以Lookup<{a: string}, "a", number>
是string
,Lookup<{a: string}, "b", number>
是number
。Tail<T>
采用元组类型T
并且 return 是第一个元素被删除的元组。所以Tail<["a","b","c"]>
是["b","c"]
.Func1
只是单参数函数的类型。ArgType<F, Else>
return如果是单参数函数,则为F
的参数类型,否则为Else
。所以ArgType<(x: string)=>number, boolean>
是string
,ArgType<123, boolean>
是boolean
。AsChain<F>
采用单参数函数的元组并尝试通过替换F
中每个函数的 return 类型将其变成一个链使用下一个函数的参数类型(最后一个函数使用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]
,这样不好Last<T>
取一个元组,return是最后一个元素,我们需要用它来表示flow()
的return类型。Last<["a","b","c"]>
是"c"
。最后,
没有限制LaxReturnType<F>
和ReturnType<F>
一样,但对F
.
好的,希望对您有所帮助;祝你好运!