泛型类中函数调用的奇怪行为

时间:2019-11-15 04:28:30

标签: 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 应该等效于功能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

1 个答案:

答案 0 :(得分:1)

差异是由于在函数上具有类型参数,而在类上具有类型参数。为了简化答案,我将删除Q[]ab上的f选项,因为没有它也会表现出相同的行为。


在分配x = a()中,Typescript需要为变量x推断类型。这需要为类型变量NQ选择具体的类型;在没有其他约束的情况下,将选择其上限,因此x的类型为M | W | undefined。这里的重点是,编译器可以自由选择上限D<M>作为Q的具体类型。

然后,在调用b(x)中,Typescript可以自由选择N = MQ = D<M>,因此不会出现类型错误。


在该类中,分配y = a()与上面的内容完全相同,因此y的类型为M | W | undefined。但是,然后在对f(y)的调用中,Typescript不能自由选择N = MQ = D<M>,因为这些N和{{1} }是类的类型参数。在这里无法推断出QN = 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)的{​​{1}}分配给类型y的{​​{1}}的参数是不安全的,因为当M | W | undefinedf或其他内容时,变量Q不是Q = D<M2>Q = D2<M>的子类型。