首页 > 解决方案 > 泛型类中函数调用的奇怪行为

问题描述

以下代码编译良好

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

标签: typescript

解决方案


差异是由函数上的类型参数与类上的类型参数引起的。为了简化答案,我将删除 上的选项Q[]a因为没有它会表现出相同的行为。bf


在赋值x = a()中,Typescript 需要推断变量的类型x。这需要为类型变量N和选择具体类型Q;在没有其他约束的情况下,选择它们的上限,因此x得到 type M | W | undefined。这里重要的一点是编译器可以自由选择上限D<M>作为Q.

那么,在调用b(x)中,Typescript同样可以自由选择N = Mand Q = D<M>,所以不会出现类型错误。


在类中,分配y = a()与上面完全相同,ytype也是如此M | W | undefined。然而,那么在对 的调用中f(y),Typescript不能随意选择N = Mand Q = D<M>,因为这些NandQ是类的类型参数。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)是不安全的,因为例如,当、 或或其他东西时,变量不是或 的子类型。yM | W | undefinedfQQ = D<M2>Q = D2<M>yQQ


推荐阅读