作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在看 the code in the streaming-iterables
包,定义 _fn
的模式然后用 fn
重载它包括 curry 版本似乎是 的案例干 , 发生在 all functions包暴露。
下面的代码是我对包的通用柯里化(Currying)重载实现的尝试:
type Leading<T extends any[]> = T extends [...infer I, infer _] ? I : never;
type Last<T extends any[]> = T extends [...infer _, infer I] ? I : never;
function curried<F extends (...args) => any>(
fn: F
): {
(...args: Parameters<F>): ReturnType<F>;
(...args: Leading<Parameters<F>>): (curried: Last<Parameters<F>>) => ReturnType<F>;
} {
return (...args) =>
args.length == fn.length - 1
? curried => fn(...[...args, curried])
: fn(...args);
}
在
generics
之前,这似乎运行良好参加进来:
function a<T>(b: string, c: number, d: T[]) {
return [b, c, ...d];
}
const f = curried(a);
f('hi', 42, [1, 2, 3]) // d: unknown[], expected: number[]
f('hi', 42) // curried: unknown[], expected: T[]
我知道
lack of support for higher order generics此时在 TS。但是
there are workarounds显然?在这个实现中可以采用这些变通方法吗?
最佳答案
我看不到任何使用 a workaround that specifies generic functions 的方法在这里帮助你。该解决方法采用通用调用签名并将其转换为通用类型……但是您需要将一个通用调用签名转换为另一个通用调用签名,而这并没有做到。无论如何,该解决方法仅适用于单个通用调用签名,不适用于传递给 curried()
的任何可能的调用签名。 .所以我会停止考虑这个。
有一些support for inferring generic functions但它只在非常特殊的情况下有效,因此很脆弱。具体来说,它在生成重载函数时不起作用,例如您正在处理 curried()
的结果。 .见 microsoft/TypeScript#33594想要查询更多的信息。如果您需要重载curried()
,那么目前是不可能的。
如果我们放宽对重载的要求,只产生从函数末尾剥离一个参数的输出,您可以为 curried()
编写调用签名。像这样:
declare function curried<I extends any[], L, R>(fn: (...args: [...I, L]) => R):
((...args: I) => (last: L) => R)
你可以验证它是这样工作的:
function z<T>(a: T, b: T, c: T) { return [a, b, c]; }
const cZ = curried(z);
// const cZ: <T>(a: T, b: T) => (last: T) => T[]
const cz2 = cZ(5, 6);
// const cz2: (last: number) => number[]
通用输入,通用输出。
z
函数是泛型的,带有类型参数
T
,等等
cZ
也是通用的,可以调用以指定
T
.万岁!
a
功能来演示。它仍然“有效”,但并不精彩。让我们试试看:
function a<T>(b: string, c: number, d: T[]) { return [b, c, ...d]; }
const f = curried(a);
// const f: <T>(b: string, c: number) => (last: T[]) => (string | number | T)[]
const f2 = f('hi', 42);
// const f2: (last: unknown[]) => unknown[]
输出
f
是通用的,好吧。但是对于类型参数
T
没有很好的推理站点.当您拨打
f('hi', 42)
,编译器需要推断
T
就在那时,但还有什么可以
T
是?它不依赖于
'hi'
或
42
.编译器不可能知道,所以它回退到
unknown
反正。
T
当您拨打电话时,您自己
f
,这使得它的通用性不那么令人印象深刻:
const f3 = f<number>('hi', 42)
// const f3: (last: number[]) => (string | number)[]
可以说是
curried(a)
输出的“正确”泛型类型应该
declare const f: (b: string, c: number) => <T>(last: T[]) => (string | number | T)[]
我搬家的地方
T
从外部函数作用域到返回的函数作用域。如果你有那个,那么你调用
f()
将产生另一个通用函数:
const f2 = f('hi', 42);
// const f2: <T>(last: T[]) => (string | number | T)[]
所以你不会有
unknown
不再:
const result = f2([1, 2, 3]);
// const result: (string | number)[]
但是编译器没有明显的方式知道它有时应该将泛型类型参数范围从输出函数移动到输出函数的输出函数。并且开发人员确实没有任何方法可以告诉编译器这样做,因为 TypeScript 缺乏谈论通用调用签名操作的表现力。
关于typescript - 为 TypeScript 中的重载捕获传递函数的泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68761267/
我是一名优秀的程序员,十分优秀!