假设定义了以下类型:

interface Shape {
  color: string;
}

现在,考虑以下方法来向该类型添加附加属性:

Extension

interface Square extends Shape {
  sideLength: number;
}

Intersection

type Square = Shape & {
  sideLength: number;
}

这两种方法的区别是什么?

而且,为了完整性和好奇心,还有其他方法可以产生类似的结果吗?

推荐答案

是的,有些差异可能与你的情况有关,也可能与你的情况无关.

也许最重要的是,当两种类型中都存在具有相同属性键的成员时,它们的处理方式有所不同.

考虑:

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

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

上面的extends会导致一个错误,因为derriving接口声明了一个属性,该属性的密钥与派生接口中的密钥相同,但签名不兼容.

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'.

然而,如果我们采用交叉口类型

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

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

没有任何错误,并进一步说明

// And this is a good thing indeed as a value conforming to the type is easily conceived
const converter: BidirectionalStringNumberConverter = {
    convert: (value: string | number) => {
        return (typeof value === 'string' ? Number(value) : String(value)) as string & number; // type assertion is an unfortunately necessary hack.
    }
}

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

const n: number = converter.convert('a'); // `convert`'s call signature comes from `BidirectionalStringNumberConverter`

Playground Link

这导致了另一个有趣的区别,interface个声明是开放式的.新成员可以添加到任何地方,因为在同一声明空间中有多个interface个同名声明是merged个.

下面是合并行为的常见用法

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) { 
    // Implementation simplified for exposition. 
    return this.map(f).reduce((xs, ys) => [...xs, ...ys], []);
  }
}

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

相比之下,存储在type声明中的交叉点类型是关闭的,不需要合并.

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

Typescript相关问答推荐

无需刷新即可让现场访客逆变

在TypeScript中使用泛型的灵活返回值类型

从typescript中的对象数组中获取对象的值作为类型'

根据上一个参数值查找参数类型,也返回类型

TypeScrip:基于参数的条件返回类型

Vue SFC中的多个脚本块共享导入的符号

错误TS2403:后续变量声明必须具有相同的类型.变量';CRYPTO';的类型必须是';CRYPATO';,但这里的类型是';CRYPATO';

如何使所有可选字段在打印脚本中都是必填字段,但不能多或少?

Angular:ngx-echart 5.2.2与Angular 9集成错误:NG 8002:无法绑定到选项,因为它不是div的已知属性'

类型';字符串|数字';不可分配给类型';未定义';.类型';字符串';不可分配给类型';未定义';

为什么S struct 类型化(即鸭子类型化)需要非严格类型联合?

在Cypress中,您能找到表格中具有特定文本的第一行吗?

为什么特定的字符串不符合包括该字符串的枚举?

类型判断数组或对象的isEmpty

如何实现允许扩展泛型函数参数的类型

如何填写Partial的一些属性并让类型系统知道它?

JSX.Element';不可分配给类型';ReactNode';React功能HOC

将带有额外id属性的Rust struct 展平回TypeScript单个对象,以返回wasm-bindgen函数

使用受保护的路由和入门来响应路由

在Nestjs中,向模块提供抽象类无法正常工作