【问题标题】:No error when generic types doesn't match in TypeScriptTypeScript 中泛型类型不匹配时不会出错
【发布时间】:2021-12-21 07:26:49
【问题描述】:

我想知道为什么我可以在configure() 方法中提供与class 不同的通用接口?在第一个no error 示例中,我提供IType<Args1> 作为MyClass 的泛型类型,然后我可以简单地用缺少道具的IArgs2 覆盖它,我没有收到任何错误。有什么办法可以保证类型完全一样吗?

interface IArgs1 {
  a: string;
  b: string;
}

interface IArgs2 {
  a: string;
}

interface IArgs3 {
  d: string;
}

interface IType<T> {
  configure(args: T): void
}

// no error - even if 'b' is missing from IArgs2
class Class implements IType<IArgs1> {
  configure(args: IArgs2) {}
}

// error - because it's missing all IArgs1 attributes
class MyClass implements IType<IArgs1> {
  configure(args: IArgs3) {}
}

【问题讨论】:

  • configure(args: IArgs2) 不会失败,如果你将它传递给带有其他属性的对象(a 除外),但如果你不传递它configure(args: IArgs3) 将失败d

标签: typescript


【解决方案1】:

这是因为T 处于逆变位置。 考虑这个例子:

interface IArgs1 {
  a: string;
  b: string;
}

interface IArgs2 {
  a: string;
}

type Covariance<T> = { box: T };

declare let args1: Covariance<IArgs1>;
declare let args2: Covariance<IArgs2>;

args1 = args2 // error
args2 = args1 // ok

您可能已经注意到 args2 不能分配给 args1。这是相反的行为。

考虑这个例子:

type Contravariance<T> = { box: (value: T) => void };

declare let args1:  Contravariance<IArgs1>;
declare let args2:  Contravariance<IArgs2>;

args1 = args2 // ok
args2 = args1 // error

现在,继承箭头发生了相反的变化。 args1 不能再分配给args2,而args2 可以分配给args1

与您的行为相同:

interface IType<T> {
  configure(args: T): void
}

因为IType&lt;T&gt; 在方差上下文中与Contravariance 相同。

这就是这里没有错误的原因:

// no error - even if 'b' is missing from IArgs2
class Class implements IType<IArgs1> {
  configure(args: IArgs2) { }
}

因为IArgs1 扩展了IArgs2

这里有一个错误:

// error - because it's missing all IArgs1 attributes
class MyClass implements IType<IArgs1> {
  configure(args: IArgs3) {}
}

因为IArgs1IArgs3 是完全不同的类型,没有任何关系。

Here你可以找到更多关于 *-variance 的话题

【讨论】:

    【解决方案2】:

    看起来类的内部接口“先出现”,然后检查所有 implements 约束。

    这意味着,在 MyClass 的情况下,configure(args: IArgs1): void 方法签名(源自 implements IType&lt;IArgs1&gt;)与 configure(args: IArgs3): void 签名(已存在于类中)进行检查,因为 IArgs3 需要IArgs1 拥有 d: string 属性,这会产生冲突和编译器错误

    相反,在 Class 的情况下,IArgs2(已存在于类中)需要 IArgs1(来自 implements IType&lt;IArgs1&gt;)具有 a: string 属性,它具有,并且因此不会产生错误


    不会撒谎,这有点令人惊讶。我希望首先对一个类施加所有 implements A, B, C 约束,然后通过实际的类实现来实现它们,但 TypeScript 不是我自己编写的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-18
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多