typescript - 泛型类中函数调用的奇怪行为
问题描述
以下代码编译良好
interface W {
x: string;
}
interface M {
x: string;
}
type D<N extends M> = N | W;
declare function a<N extends M, Q extends D<N> = D<N>>(): Q | Q[] | undefined;
declare function b<N extends M, Q extends D<N> = D<N>>(p?: Q | Q[]): void;
const x = a();
b(x);
b
当我将函数放在一个类中时,事情变得有趣了:
export class C<N extends M, Q extends D<N>> {
f(p?: Q | Q[]): void {}
g() {
const y = a();
this.f(a());
this.f(y);
}
}
在这里,C.f
应该等价于 function b
。但是,我在行上收到类型错误this.f(y)
。有趣的是,该行this.f(a())
编译得很好。
Argument of type 'W | M | (W | M)[] | undefined' is not assignable to parameter of type 'Q | Q[] | undefined'.
Type 'W' is not assignable to type 'Q | Q[] | undefined'.
Type 'W' is missing the following properties from type 'Q[]': length, pop, push, concat, and 28 more.
我不明白这个错误,因为类型Q
应该等同于W | N
所以怎么W
不能分配给Q
?
解决方案
差异是由函数上的类型参数与类上的类型参数引起的。为了简化答案,我将删除 上的选项Q[]
,a
因为没有它会表现出相同的行为。b
f
在赋值x = a()
中,Typescript 需要推断变量的类型x
。这需要为类型变量N
和选择具体类型Q
;在没有其他约束的情况下,选择它们的上限,因此x
得到 type M | W | undefined
。这里重要的一点是编译器可以自由选择上限D<M>
作为Q
.
那么,在调用b(x)
中,Typescript同样可以自由选择N = M
and Q = D<M>
,所以不会出现类型错误。
在类中,分配y = a()
与上面完全相同,y
type也是如此M | W | undefined
。然而,那么在对 的调用中f(y)
,Typescript不能随意选择N = M
and Q = D<M>
,因为这些N
andQ
是类的类型参数。N = M
在这里推断和是不合理的Q = D<M>
,因为有人:
- 可以编写
class M2 implements M { ... }
和创建一个C<M2, D<M2>>
对象。 - 可以编写
type D2<N extends M> = D<N> & { ... }
和创建一个C<M, D2<M>>
对象。
因此,在调用中,将类型分配给类型的参数f(y)
是不安全的,因为例如,当、 或或其他东西时,变量不是或 的子类型。y
M | W | undefined
f
Q
Q = D<M2>
Q = D2<M>
y
Q
Q