TypeScript中扩展和相交接口之间的区别?

时间:2018-10-06 16:56:20

标签: typescript types intersection extends

假设定义了以下类型:

interface Shape {
  color: string;
}

现在,考虑使用以下方法向此类型添加其他属性:

扩展

interface Square extends Shape {
  sideLength: number;
}

路口

type Square = Shape & {
  sideLength: number;
}

两种方法有什么区别?

出于完整性和好奇心的考虑,还有其他方法可以产生可比的结果吗?

1 个答案:

答案 0 :(得分:9)

是的,在您的方案中可能存在或可能存在差异。

也许最重要的是,两种类型的属性键相同的成员在两种类型中的处理方式有所不同。

考虑:

interface NumberToStringConverter {
  convert: (value: number) => string;
}

interface BidirectionalStringNumberConverter extends NumberToStringConverter {
  convert: (value: string) => number;
}

上面的extends导致错误,因为派生接口声明的属性与派生接口具有相同的键,但签名不兼容。

error TS2430: Interface 'BidirectionalStringNumberConverter' incorrectly extends interface 'NumberToStringConverter'.

  Types of property 'convert' are incompatible.
      Type '(value: string) => number' is not assignable to type '(value: number) => string'.
          Types of parameters 'value' and 'value' are incompatible.
              Type 'number' is not assignable to type 'string'.

但是,如果我们使用交叉点类型

interface NumberToStringConverter = {
    convert: (value: number) => string;
}

type BidirectionalStringNumberConverter = NumberToStringConverter & {
    convert: (value: string) => number;
}

没有任何错误并进一步给出

declare const converter: StringToNumberConverter;

converter.convert(0); // `convert`'s call signature comes from `NumberToStringConverter`

converter.convert('a'); // `convert`'s call signature comes from `BidirectionalStringNumberConverter`

// And this is a good thing indeed as a value conforming to the type is easily conceived

const converter: StringToNumberConverter = {
  convert: (value: string | number) =>
    typeof value === 'string'
      ? Number(value)
      : String(value)
}

这导致另一个有趣的区别,interface声明是开放式的。可以在任何位置添加新成员,因为在同一声明空间中具有相同名称的interface声明被合并

这是合并行为的常见用法

lib.d.ts

interface Array<T> {
    // map, filter, etc.
}

array-flat-map-polyfill.ts

interface Array<T> {
    flatMap<R>(f: (x: T) => R[]): R[];
}

if (typeof Array.prototype.flatMap !== 'function') {
    Array.prototype.flatMap = function (f) {
        return this.map(f).reduce((xs, ys) => [...xs, ...ys], []);
    }
}

请注意如何不存在extends子句,尽管接口是在单独的文件中指定的,但它们都在全局范围内,并且按名称合并为具有两组成员的单个逻辑接口声明。 (对于语法范围稍有不同的模块范围的声明,也可以这样做

通过对比,type声明中存储的交集类型是封闭的,无需合并。

有很多很多不同之处。您可以在TypeScript手册中阅读有关这两种构造的更多信息。 InterfacesAdvanced Types部分特别相关。