我试图找出如何返回一个链接API的目的推断参数类型。下面是一个简单的例子:
type NoInfer<T> = [T][T extends unknown ? 0 : never]
interface Alpha<Foo extends string> {
foo: Foo
bar: `Depends on ${NoInfer<Foo>}`
}
interface Chain<ACC> {
bravo: <T extends { [K in keyof T]: string }>(xs: { [K in keyof T]: Alpha<T[K]> }) => void
// instead of void, return Chain<[***inferred type of xs***, ...ACC> -------------^^^^
}
在上面,bravo
参数类型工作得很好,但是缺少通过链构建类型上下文的能力。在每次调用bravo
时,如何在返回类型中捕获xs
的推断类型并递归地传递给泛型Chain
?
操场上联系
注意,我已经发现,使返回类型为{ [K in keyof T]: Alpha<T[K]> }
似乎工作。TS似乎有这样的行为:给定的输入(参数参数)将决定返回位置的类型是什么。
然而,完全的复制充其量是相当难以读取、维护等。有没有一种解决方案不会沿着这条路走下去?
你真正想要的是创建内联类型别名,如microsoft/TypeScript#30979所述。不幸的是,该功能不是该语言的一部分。如果是的话,也许我可以告诉你写
// ⚠☠ THE FOLLOWING CODE IS NOT VALID TS; DON'T TRY IT ☠⚠
interface Chain<A extends unknown[]> {
bravo: <T extends { [K in keyof T]: string }>(
xs: type U = {[K in keyof T]: Alpha<T[K]>}
) => Chain<[U, ...A]>
}
然后继续。如果没有这样的功能,我所能给你的就是变通办法。
最普遍适用的解决方案是创建一个实用程序类型;也就是说,在需要它的地方之外的常规类型别名:
type MapAlpha<T extends { [K in keyof T]: string }> =
{ [K in keyof T]: Alpha<T[K]> }
然后在你需要的地方多次使用它:
interface Chain<A extends unknown[]> {
bravo: <T extends { [K in keyof T]: string }>(
xs: MapAlpha<T>) => Chain<[MapAlpha<T>, ...A]>
}
这需要比您想要的多一点的代码,但是您正在做的事情是显而易见的。此外,如果类型是你确实需要多次使用的东西,给它一个有意义的名字可能是值得的(MapAlpha
对于将Alpha
映射到对象类型上的东西在我看来是合理的)。
作为第二种方法:如果在作用域中存在您想要的类型的值,则可以使用typeof
操作符引用其类型。即使值是一个函数参数,也可以这样做:
interface Chain<A extends unknown[]> {
bravo: <T extends { [K in keyof T]: string }>(
xs: { [K in keyof T]: Alpha<T[K]> }
) => Chain<[typeof xs, ...A]>
}
此处typeof xs
与{ [K in keyof T]: Alpha<T[K]> }
相同。
这样更简洁,但可能会让读者更困惑。但主要问题是,并不总是能够直接找到您想要的类型的值,您可能不得不退回到实用程序类型定义。
有时还有其他解决方法,包括在更广泛的范围内使用额外的类型参数来充当内联类型别名,但对于给定的示例,我无法提出任何我想建议的方法。我的意思是,以下类型的作品:
interface Chain<A extends unknown[]> {
bravo: <T extends { [K in keyof T]: string }, U>(
xs: { [K in keyof T]: Alpha<T[K]> } & U) => Chain<[U, ...A]>
}
但是它依赖于U
是从xs
和T
分别推断出来的,因此类型不一定相同。我总是建议在玩这样的推理游戏之前创建一个实用工具类型。
Playground链接到代码