首页 > 解决方案 > 为什么下面的 TypeScript 程序不抛出类型错误?

问题描述

考虑以下程序

interface Eq<A> {
  eq(this: A, that: A): boolean;
};

class Pair<A> implements Eq<Pair<A>> {
  constructor(public x: A, public y: A) {}

  eq(this: Pair<A>, that: Pair<A>): boolean {
    return this.x === that.x && this.y === that.y;
  }
}

class Triple<A> implements Eq<Triple<A>> {
  constructor(public x: A, public y: A, public z: A) {}

  eq(this: Triple<A>, that: Triple<A>): boolean {
    return this.x === that.x && this.y === that.y && this.z === that.z;
  }
}

const eq = <A extends Eq<A>>(x: A, y: A): boolean => x.eq(y);

console.log(eq(new Pair(1, 2), new Triple(1, 2, 3)));
console.log(eq(new Triple(1, 2, 3), new Pair(1, 2)));

我原以为 TypeScript 编译器会抱怨最后两行,因为您不应该将eq函数应用于不同类型的两个值。但是,TypeScript 编译器不会为上述程序抛出任何类型错误。上述程序的结果是truefalse

为什么 TypeScript 编译器不会为上述程序抛出类型错误?我们怎样才能让它正确地捕捉到这些类型的错误呢?

标签: typescriptgenericsinterfacetypeerrortype-safety

解决方案


该程序的编译是由于TypeScript 中使用的结构子类型(与其他编程语言中经常出现的名义子类型相反)。

请注意,您的Triple类是可以分配给类型变量的Pair

const p: Pair<number> = new Triple(1, 2, 3);

在您的示例中:

console.log(eq(new Pair(1, 2), new Triple(1, 2, 3)));
console.log(eq(new Triple(1, 2, 3), new Pair(1, 2)));

的类型eq推断为:

const eq: <Pair<number>>(x: Pair<number>, y: Pair<number>) => boolean

如上所示,Triple是类型参数的有效参数Pair,因此一切都可以干净地编译。

您可以向您的类添加不同的私有字段来模拟名义子类型。在此特定示例中,您可以选择以下两个选项之一:

  • 添加额外的标记字段
  • make x, y, zprivate 并提供 getter

请参阅我可以强制 TypeScript 编译器使用名义类型吗?


推荐阅读