我的 TypeScript 代码中有以下常见场景:
interface TopLevelInterface<A extends BottomLevelInterface<B>, B>
以及
BottomLevelInterface
的实现看起来像这样:class BottomLevelClass implements BottomLevelInterface<MyType>
我的问题是:在实现
TopLevelInterface
时我不仅需要为 A
传递类型参数这将是 BottomLevelClass
, 也是 B
的第二种类型参数这将是 MyType
在上面的例子中。为什么我需要指定
B
通过查看 BottomLevelClass
的类型参数可以很容易地推断出这一点。 ?例如,在实现
TopLevelInterface
时我需要指定以下内容:class TopLevelClass implements TopLevelInterface<ConcreteBottomLevel, MyType>
而不是应该足够的较短版本:
class TopLevelClass implements TopLevelInterface<ConcreteBottomLevel>
为什么这是必要的?如何通过查看第一个参数来推断第二个类型的参数?我想出的唯一解决方案是使用
infer
在带有默认分配的 TypeScript 2.8+ 中。此解决方案如下所示:interface TopLevelInterface<A extends BottomLevelInterface<B>,
B = A extends BottomLevelInterface<infer _B> ? _B : any>
但是,我无法将其正确应用于三层类层次结构。在下面的 StackBlitz 中,您可以看到我无法获得属性
model
的正确类型约束。的 TopLevelClass
.应该推断为类型 SomeType
而是推断为 never
.在 MiddleLevelClass
它确实工作正常。https://stackblitz.com/edit/typescript-pcxnzo?file=infer-generics.ts
任何人都可以解释这个问题或达到预期结果的更好方法吗?
最佳答案
MiddleLevelClass
中的故意错误正在影响 TopLevelClass
的行为,所以对于一个有效的测试,我们应该基于 TopLevelClass
在正确的 MiddleLevelClass
上并使用单独的 BadMiddleLevelClass
来证明那里的错误。
您的第一个问题是您的条件类型具有 any
的“其他”案例| ,这将倾向于隐藏错误。 never
往往会更好并且被普遍使用,尽管完整的解决方案需要 a unique invalid
type .
有了这些变化,似乎主要的问题是当你写 TopLevelInterface<MiddleLevelClass>
时并且 TypeScript 尝试评估 B = M extends MiddleLevelInterface<infer _B> ? _B : never
,没有对 _B
的推论因为B
MiddleLevelInterface
实际并未使用参数或 MiddleLevelClass
.见 this FAQ .添加使用 B
的虚拟可选属性解决问题。 (我猜您在实际应用程序中将 B
用于某些东西,否则您就不会声明 B
,但您删除了简化示例中的使用?)
新代码:
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;
}
基于 jcalz 建议的原始问题的替代解决方案(谢谢!):不要使用多个类型参数,而是使用辅助类型别名来确定
T
输入来自 B
类型和 B
输入来自 M
每次需要时都键入。这是代码:export class SomeType {
x: string;
}
export interface BottomLevelInterface<T> {
model : T;
}
export class BottomLevelClass implements BottomLevelInterface<SomeType> {
model: SomeType;
}
type TfromB<B extends BottomLevelInterface<any>> = B extends BottomLevelInterface<infer T> ? T : never;
export interface MiddleLevelInterface<B extends BottomLevelInterface<any>> {
_dummy_B?: B;
model: TfromB<B>;
}
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;
}
type BfromM<M extends MiddleLevelInterface<any>> = M extends MiddleLevelInterface<infer B> ? B : never;
export interface TopLevelInterface <M extends MiddleLevelInterface<any>> {
model: TfromB<BfromM<M>>;
}
export class TopLevelClass implements TopLevelInterface<MiddleLevelClass> {
// now there is an error here
model: string;
}
如果依赖条件类型而不是显式类型参数使超出这个简单示例的某些操作变得更加困难,我不会感到惊讶。你可以试试看。
关于typescript - 在多个级别上推断 TypeScript 中的泛型类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52905336/