如何在 TypeScript 中正确输入转换函数?

问题描述 投票:0回答:1

下面是一个最小的转换实现,但它的输出类型不正确。寻找需要改变的地方来纠正它。这是 TS Playground 中的示例,其中包含转导和传递给它的所有参数。 如果有帮助的话,这里有一个“可能更简单的示例游乐场”。我不确定它是否过于简单化了。 /* transducer */ const map = <VOut,VIn,KIn>(mapper: (v: VIn, k?:KIn) => VOut) => <A, R>(nextReducer: (acc: A, v: VOut, k?:KIn) => R) => <V extends VIn>(acc: A, value: V, key?:KIn) => nextReducer(acc, mapper(value,key), key); /* final reducer */ const toSet = <V,K,A>(acc:A=new Set() as A, v: V, k?:K) => { (acc as Set<V>).add(v); return acc as Set<V>; }; /* test map and toSet by themselves */ // test correct - mapNum is Set<string> const mapStr = map((v: number) => `${v}`)(toSet)(undefined,1,'a'); // test correct - mapNum is Set<number> const mapNum = map((v: number) => v + 2)(toSet)(undefined,1,'a'); // @ts-expect-error test correct - string 'aa' passed to number mapper should error const mapStrError = map((v: number) => v + 2)(toSet)(undefined,'aa','a'); /* transduce */ const transduce = <AccIn,AccOut,VIn,VOut,KIn,KOut>( collection: Map<KIn, VIn>, transducer: < NextReducer extends (a:any,v:any,k?:any)=>any >(nextReducer:NextReducer)=>((a:AccIn,v:VIn,k?:KIn)=>AccOut), finalReducer: (a:AccOut|undefined,v:VOut,k?:KOut)=>AccOut, ) => { let combinedReducer = transducer(finalReducer); let acc = undefined as AccOut; for (const [key, value] of collection) { acc = combinedReducer(acc as unknown as AccIn, value, key); } return acc; } /* test transduce */ // @ts-expect-error correct - string collection passed to number mapper should error const transduceStr = transduce(new Map([['a', 'aa']]), map((v: number) => v * 2), toSet); // incorrect - transduceNum is unknown. Should be Set<number> like mapNum const transduceNum = transduce(new Map([['a', 1]]), map((v: number) => v * 2), toSet);

	
typescript typescript-generics higher-order-functions
1个回答
0
投票
microsoft/TypeScript#30727

获取权威答案。 TypeScript 推断高阶泛型

的能力非常有限。理论上,它可以使用完整的“统一”算法,该算法能够处理对其他通用函数进行操作的任意通用函数并产生您正在寻找的结果。

microsoft/TypeScript#30134 对此有一个讨论。 当前的推理算法本质上是启发式的,并且针对处理各种现实世界代码进行了优化,特别是处理“部分编写”代码,以便 IDE 可以比完全统一更容易地完成和建议事情。请参阅 语言架构师在 microsoft/TypeScript#17520 上的评论 因此,无论好坏,TypeScript 都无法按照您想要的方式推断您的泛型类型参数,并且这在未来不太可能发生重大变化。 您需要以某种方式解决该问题。一种通用方法是手动指定预期的泛型类型参数,而不是让 TypeScript 推断它们。这很乏味,特别是如果您的泛型函数具有... six... 类型参数,但其行为始终是可预测的,并让您知道是否有真正的错误: const transduceNum = transduce<Set<number>, Set<number>, number, number, string, string>( new Map([['a', 1]]), map((v: number) => v * 2), toSet ); // okay, Set<number> as desired, no error

可能有一些方法可以重新排序泛型类型参数并使用

默认类型参数,以便您可以指定更少的类型参数并仍然获得您想要的类型。

或者您可以使用
实例化表达式
将泛型函数参数(如

toSet)转换为特定的非泛型函数:

const transduceNum = transduce( new Map([['a', 1]]), map((v: number) => v * 2), toSet<number, string, Set<number>> ); // okay, Set<number> as desired, no error

或者您可以通过完全重构来解决问题,但这超出了所提出的问题的范围,所以我不会进一步离题。

Playground 代码链接

    

© www.soinside.com 2019 - 2024. All rights reserved.