gpt4 book ai didi

typescript - 为 TypeScript 中的重载捕获传递函数的泛型类型

转载 作者:行者123 更新时间:2023-12-04 14:05:15 25 4
gpt4 key购买 nike

我在看 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 缺乏谈论通用调用签名操作的表现力。
这将需要在 microsoft/TypeScript#1213 中请求的排序更高的类型。或在 microsoft/TypeScript#17574 中请求的排序的通用值...或者甚至可能是 microsoft/TypeScript#14466 中要求的那种存在量化的泛型.或者也许他们三个。或者是其他东西。然而,现在看来,这似乎遥不可及。

所以你去。如果你放宽你的要求并忍受一些边缘情况,你可以接近你想要的。如果你不能,那么我想我们可能不得不等到 future 版本的 TypeScript 支持更强大的泛型函数类型操作。
Playground link to code

关于typescript - 为 TypeScript 中的重载捕获传递函数的泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68761267/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com