接口和类型别名之间在语义上有什么区别吗?

时间:2019-02-05 14:01:13

标签: typescript

我可以使用接口和类型别名来做大多数相同的事情。

例如

类可以实现接口或键入别名

interface Shape {
    area(): number;
}

type Perimeter = {
    perimeter(): number;
}

class Rectangle implements Shape, Perimeter {
}

可以将它们组合以创建新的接口/类型别名

interface A {
    a: string;
}

type B = {
    b: string
}

interface C extends B {
    c: string;
}

type D = A & {
    d: string;
}

接口和类型注释之间是否存在语义差异?

2 个答案:

答案 0 :(得分:1)

接口可以扩展其他接口,也可以*实现类。接口也可以利用declaration merging

type A = {
  a: string;
}

type A = { // <-- error: duplicate identifier A
  b: string;
}

interface A {
  a: string;
}

interface A { // <-- okay, A is { a: string, b: string } 
  b: string; 
}

编辑:已更改为*实现

edit 2:相交与延伸不同。请考虑以下内容:

interface A {
  num: number;
  str: string; 
}

type B = A & { // <-- this is okay, but overwrites the num property type to number & string
  arr: any[];
  num: string;
}

interface C extends A { // <-- error: C incorrectly extends A
  num: string;
}

编辑3:对于某些人来说,另一个潜在的重大差异(虽然不一定是语义差异)是类型在工具提示中枚举(至少在vscode中),而接口则没有。

答案 1 :(得分:0)

interfacetype之间的技术差异为well-described here

但是,对于同时使用typeinterface的情况,根本没有语义上的差异。

关于TypeScript中的interface继承和type交集

在TypeScript中,接口之间的层次结构只是定义接口的一种方法。但是,一旦定义它们,接口之间就没有真正的父子关系。例如:

interface Named {
    name: string
}
interface Person extends Named {
    age: number
}
interface Animal {
    name: string
    age: number
}

此处PersonAnimal是同一类型。定义它们后,当其他代码使用它们时,编译器将以完全相同的方式对其进行处理:

function useNamed(named: Named) {
}
let p: Person = /* ... */
let a: Animal = /* ... */
useNamed(p) // OK
useNamed(a) // also OK, because if 'Animal' is compatible with
            // `Named` then it is a `Named`

这就是为什么也可以使用交集类型创建相同类型的原因:

type Engine = Named & {
    age: number
}

根据规范:

  

交叉点类型 表示同时具有多种类型的值。 A B 的交集类型的值是 A 类型和类型的 both 的值> B 。 (来源:TypeScript Specification

我们的Engine类型既是Named也是附加的定义:它在语义上与接口继承相同。这里的Engine类型与PersonAnimal完全相同。