我的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
任何人都可以解释这个问题或获得所需结果的更好方法吗?
答案 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
B
或MiddleLevelInterface
实际未使用该参数。参见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
如果对条件类型的依赖而不是显式类型参数使某些超出此简单示例的操作变得更加困难,我不会感到惊讶。您可以尝试看看。