我有一个接受解析器函数的函数 api。解析器是通用的,例如,返回与输入相同的类型(并非总是这样,只是为了简化)。我...
我有一个函数 api
,它接受一个解析器函数。解析器是通用的,例如,返回与输入相同的类型(并非总是这样,只是为了简化)。
在 api
函数中我知道将传递给解析器输入什么类型( string
为了简单起见,实际上是通过模板化传递给 api
函数的其他参数来确定的)。
如何 api
根据 parse
为模板指定参数的返回类型来设置函数的返回类型?
// in the lib
type Parse = <T>(val: T) => any
let api = <P extends Parse>(
parse: P,
) =>
parse("xxx") as ???
// somewhere
let identity = <T>(val: T): T => val
let res = api(identity)
我尝试过的:
let api = <P extends Parse>(
parse: P,
) =>
parse("xxx") as ReturnType<P<string>> // error "Type P is not generic"
// we can't specify type arguments of generic function type
let api = <P extends Parse>(
parse: P,
) =>
parse("xxx") as P extends (val: string) => infer O ? O : never // unknown
let api = <P extends Parse>(
parse: P,
) =>
parse("xxx") as ReturnType<typeof parse<string>> // any, instantiation expression don't work either
我想指出的是,我无法访问该 parse
函数的实现。它可以是任何适合该 Parse
类型的函数(如果需要,我可以更改它)。
我不知道该说什么才能避免重复。如果输出类型取决于输入类型,这是不可能的,这就是 ms/TS#40179 的要点。当然,如果返回类型不依赖于泛型类型参数,它会起作用,但这不是你要问的。你只是想让我重申我的评论,然后再告诉我它是否完全解决了你的问题?还是说我错过了你的问题的一些重要方面,这使得它与 ms/TS#40719 不同?
这超出了 TypeScript 的能力。
无法提出这样的问题:“对于给定的泛型函数类型 类型 F = <T>(⋯)=>⋯
类型参数实例化类型参数, T
函数的类型将是什么 U
?”如果您有一个实际的泛型函数 表达式 f
而不仅仅是 type F
,则可以使用 instantiation expression 实例化表达式 f<U>
。但是无法纯粹在类型级别执行此操作。请参阅 microsoft/TypeScript#47607 上的此评论 :
[I]实例化表达式目前在类型领域没有等效项。我们不能仅仅采用
T<X>
类型实例化语法,因为它已经意味着其他东西(即,将类型参数应用于泛型类型T
)。
也许有一天会有这样一种类型级别的解决方案,使用一些特殊的语法,例如 F«U»
或类似的东西。但目前还不可能。
但情况甚至更糟。你有一个 generic 限制 P
为的 泛型 类型参数 Parse
。但这并不意味着这 P
是一个泛型函数类型。例如,你可以调用
api((x: unknown) => 123); // okay
TypeScript 中没有办法抽象地谈论“调用签名只有一个类型参数的泛型函数类型”。这将需要类似 更高类型的类型 中所述的 microsoft/TypeScript#1213 。因此, P
不会接受类型参数,因此即使使用假设的 F«U»
语法, P«string»
也不会被允许。或者也许它 会 被允许,但对非泛型函数不做任何事情?
即使如此,情况也会变得更糟,因为你只是假设类型参数是第一个函数参数的类型。否则 P«string»
可能与调用无关 parse("xxx")
。想象一下有人这样调用:
const g = <T,>(x: unknown, y?: T) => y;
// const g: <T>(x: unknown, y?: T | undefined) => T | undefined
api(g)
你不能指望 g<string>
与返回类型有任何关系 parse("xxx")
.
实际上,您的代码示例看起来根本不像您想询问泛型。相反,您想问“给定一个 f
类型的 F
一个类型的 u
有效参数 U
,的类型是什么 f(u)
?”这将需要所谓的 调用类型 ,TypeScript 也不支持,因此这仍然超出了 TypeScript 的能力。在 microsoft/TypeScript#40179 。也许将来会有一些语法来 F(U)
表示这一点。在这样一个假设的世界中,您的示例将是
// NOT VALID TS, DON'T TRY THIS:
let api = <F extends (x: string) => any>(parse: F): F(string) =>
parse("xxx");
然后,当你调用时, api(identity)
你 string
会 unknown
像预期的那样得到结果,而不是 。而且它不需要 输入是一个通用函数,只要它可以像调用一样调用 (x: string) => any
。据推测这也可以 overloads 像 {(x: string): number; (x: number): string}
.
但同样,它不是语言的一部分。目前你能得到的最接近的是 the ReturnType
utility type ,它完全无法解析泛型类型参数或重载调用签名。
总之,这是不可能的。
游乐场链接到代码