在多个级别的TypeScript中推断通用类型参数

时间:2018-10-20 11:48:28

标签: typescript generics type-inference

我的TypeScript代码中有以下常见情况:

interface TopLevelInterface<A extends BottomLevelInterface<B>, B>

以及BottomLevelInterface的实现,如下所示:

class BottomLevelClass implements BottomLevelInterface<MyType>

我的问题是:在实现TopLevelInterface时,我不仅需要传递A的{​​{1}}的类型实参,还需要传递BottomLevelClass的第二种类型实参在上面的示例中为B

为什么我需要指定MyType,而通过查看B的类型实参可以很容易地推断出这一点?

例如,在实现BottomLevelClass时,我需要指定以下内容:

TopLevelInterface

代替应该足够的简短版本:

class TopLevelClass implements TopLevelInterface<ConcreteBottomLevel, MyType>

为什么这是必需的?如何查看第一个参数来推断第二个参数?我想到的唯一解决方案是在TypeScript 2.8+中使用class TopLevelClass implements TopLevelInterface<ConcreteBottomLevel> 并使用默认分配。此解决方案如下所示:

infer

但是,我无法在三层类层次结构中正确地应用它。在下面的StackBlitz中,您可以看到我无法获得interface TopLevelInterface<A extends BottomLevelInterface<B>, B = A extends BottomLevelInterface<infer _B> ? _B : any> 的属性model的正确类型约束。应该将其推断为类型TopLevelClass,而应将其推断为SomeType。在never,它可以正常工作。

https://stackblitz.com/edit/typescript-pcxnzo?file=infer-generics.ts

任何人都可以解释这个问题或获得所需结果的更好方法吗?

1 个答案:

答案 0 :(得分:1)

MiddleLevelClass中的故意错误正在影响TopLevelClass的行为,因此对于有效的测试,我们应以正确的TopLevelClass为基础的MiddleLevelClass并使用单独的{ {1}}演示那里的错误。

您的第一个问题是您的条件类型的情况为BadMiddleLevelClass的其他情况,这往往会隐藏错误。尽管完整的解决方案需要a unique invalid type,但any往往会更好并且更常用。

有了这些更改,似乎主要的问题是,当您编写never且TypeScript尝试求值TopLevelInterface<MiddleLevelClass>时,B = M extends MiddleLevelInterface<infer _B> ? _B : never没有任何推论,因为_B BMiddleLevelInterface实际未使用该参数。参见this FAQ。添加使用MiddleLevelClass的虚拟可选属性可以解决此问题。 (我猜您在实际应用程序中使用了B,否则您不会声明B,但是在简化示例中删除了该用法?)

新代码:

B

基于jcalz的建议(谢谢!)解决原始问题的替代方法:代替使用多个类型参数,使用帮助程序类型别名从export class SomeType { x: string; } export interface BottomLevelInterface<T> { model : T; } export class BottomLevelClass implements BottomLevelInterface<SomeType> { model: SomeType; } export interface MiddleLevelInterface<B extends BottomLevelInterface<T>, T = B extends BottomLevelInterface<infer _T> ? _T : never> { _dummy_B?: B; model: T; } export class MiddleLevelClass implements MiddleLevelInterface<BottomLevelClass> { _dummy_B?: BottomLevelClass; model: SomeType; } export class BadMiddleLevelClass implements MiddleLevelInterface<BottomLevelClass> { // here we correctly see an error from TypeScript service, as 'string' cannot be applied to 'SomeType' model: string; } export interface TopLevelInterface <M extends MiddleLevelInterface<B, T>, B extends BottomLevelInterface<T> = M extends MiddleLevelInterface<infer _B> ? _B : never, T = B extends BottomLevelInterface<infer _T> ? _T : never> { model: T; } export class TopLevelClass implements TopLevelInterface<MiddleLevelClass> { // now there is an error here model: string; } 类型和{{每次需要时,从T类型中选择1}}类型。这是代码:

B

如果对条件类型的依赖而不是显式类型参数使某些超出此简单示例的操作变得更加困难,我不会感到惊讶。您可以尝试看看。