rjh*_*ort 4 functional-programming currying typescript
我有一个map处理承诺的咖喱版本。它需要两个参数,一次一个。它需要两个类型参数来让调用者提供无法推断的部分。但是,实际上调用者总是必须提供它们,因为它们传入的转换函数中不会推断类型。该函数如下所示:
/**
* @description
* Like `ramda.map`, but handles an iterator that returns a promise (or not).
*
* @example
* await mapP((x: number) => Promise.resolve(x + 1))([1, 2, 3]) // -> [2, 3, 4]
*/
export const mapP = <T, R>(xf: (value: T) => Promise<R> | R) => (
data: T[],
): Promise<R[]> => pipe(when(isNil, always([])), map(xf), allP)(data)
Run Code Online (Sandbox Code Playgroud)
这是我调用它,您可以看到x类型系统不知道 。
如何修复我编写函数以使类型工作的方式(不放弃柯里化 - 我知道如果不柯里化它会弄清楚)?
我将使用以下声明:
declare const mapP: <T, R>(xf: (value: T) => Promise<R> | R) => (
data: T[],
) => Promise<R[]>;
Run Code Online (Sandbox Code Playgroud)
这与您的版本相同,但不担心实现。无论如何,您遇到了以下问题无法推断x回调中的类型:
const res = mapP(x => x.name)(data); // error!
// x is unknown ----> ~
Run Code Online (Sandbox Code Playgroud)
期望编译器可能推断出它必须是 type并不是完全没有道理的,因为is 是 type 。有一种上下文类型这样的东西,在这种情况下,编译器将通过查看某些东西的使用方式并确定它应该被声明为哪种类型才能“及时”推断类型。不幸的是,编译器通过多个函数调用向后执行此操作有点费力。那好吧。xFoodataFoo[]
钻营的一大吸引力,在我看来,是能够部分地应用功能,然后使用该部分应用功能后,就像这样:
const f = mapP(x => x.name); // error!
// x is unknown --> ~
// later
const res2 = f(data);
Run Code Online (Sandbox Code Playgroud)
在这种情况下,期望编译器知道关于 的任何有用信息是难以置信的x,尤其是考虑到其他可能的调用,例如:
const res3 = f([{ name: false }]);
Run Code Online (Sandbox Code Playgroud)
哪里x应该是 a{name: boolean}而不是 a Foo。如果你的意图,x => x.name是x应该是Foo,你需要一个意图传达给编译器,通过类型注释:
const res4 = mapP((x: Foo) => x.name)(data); // okay Promise<string[]>
Run Code Online (Sandbox Code Playgroud)
这是我向您推荐的问题的解决方案;您不需要开发人员手动指定T和R何时调用mapP(). 相反,你被标注回调的参数,这样编译器可以推断T和R本身,它的作用。
请注意,您甚至可以通过使用通用回调来获得更多幻想并传达“我希望回调应用于具有属性的任何内容name并返回该类型的值” :
const g = mapP(<T>(x: { name: T }) => x.name);
const res5 = g(data); // Promise<string[]>;
const res6 = g([{ name: false }]); // Promise<boolean[]>;
Run Code Online (Sandbox Code Playgroud)
在这里编译器可以使用TS3.4 中引入的一些高阶类型推断来查看它g()本身是一个泛型函数。
因此,备份:如果您的用例真的是通过调用它而不持有对它的引用来立即使用部分应用的函数,那么您可能应该在不进行柯里化的情况下这样做。“两全其美”的方法可能是一个重载的混合函数,它既是柯里化的又是非柯里化的:
function mapQ<T, R>(xf: (value: T) => Promise<R> | R, data: T[]): Promise<R[]>;
function mapQ<T, R>(xf: (value: T) => Promise<R> | R): (data: T[]) => Promise<R[]>;
function mapQ<T, R>(
xf: (value: T) => Promise<R> | R,
data?: T[]
): ((data: T[]) => Promise<R[]>) | Promise<R[]> {
return data ? mapQ(xf)(data) : mapQ(xf);
}
Run Code Online (Sandbox Code Playgroud)
然后,当且仅当您打算稍后使用该函数时,您才可以使用它柯里化:
const res7 = mapQ(x => x.name, data); // okay Promise<string[]>
const h = mapQ((x: Foo) => x.name);
const res8 = h(data); // okay Promise<string[]>
Run Code Online (Sandbox Code Playgroud)
好的,希望有帮助;祝你好运!
| 归档时间: |
|
| 查看次数: |
275 次 |
| 最近记录: |