typescript 如何在返回类型位置使用推断参数类型而不完全重复?

c9qzyr3d  于 2023-04-13  发布在  TypeScript
关注(0)|答案(1)|浏览(85)

我试图弄清楚如何返回用于链接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
Playground链接
注意,我发现返回类型为{ [K in keyof T]: Alpha<T[K]> }似乎是可行的。TS似乎有这样的行为,即给定的输入(参数)将决定返回位置的类型。
然而,彻底的复制充其量是很难阅读,维护等,有没有一个解决方案,不走这条路?

bt1cpqcv

bt1cpqcv1#

您真正想要的是创建内联类型别名,如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对于mapsAlpha在对象类型上似乎是合理的)。
作为第二种方法:如果在作用域中有一个你想要的类型的值,你可以使用the typeof operator来引用它的类型。即使该值是一个函数参数,这也会起作用:

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]>
}

但是它依赖于Uxs中推断出来,而T是从xs中推断出来的,因此类型不一定相同。
Playground链接到代码

相关问题