TypeScript中的这些语句(接口与类型)有什么区别?

interface X {
    a: number
    b: string
}

type X = {
    a: number
    b: string
};

当前回答

文件已经解释了

一个区别是,接口创建了一个新名称,该名称在任何地方都可以使用。类型别名不会创建新名称-例如,错误消息不会使用别名名称。在旧版本的TypeScript中,类型别名无法从扩展或实现(也无法扩展/实现其他类型)。从2.7版起,可以通过创建新的交叉点类型来扩展类型别名另一方面,如果不能用接口表达某种形状,并且需要使用联合或元组类型,则通常使用类型别名。

接口与类型别名

其他回答


何时使用类型?


通用转换

将多个类型转换为单个泛型类型时,请使用该类型。

例子:

type Nullable<T> = T | null | undefined
type NonNull<T> = T extends (null | undefined) ? never : T

类型别名

我们可以使用该类型为长类型或复杂类型创建别名,这些类型既难以阅读,又不方便反复键入。

例子:

type Primitive = number | string | boolean | null | undefined

创建这样的别名使代码更加简洁易读。


类型捕获

当类型未知时,使用该类型捕获对象的类型。

例子:

const orange = { color: "Orange", vitamin: "C"}
type Fruit = typeof orange
let apple: Fruit

在这里,我们得到了未知类型的橙子,称其为水果,然后使用水果创建一个新型的安全对象苹果。


何时使用界面?


多态性

接口是实现数据形状的契约。使用该接口明确表示它将被实现并用作关于如何使用对象的约定。

例子:

interface Bird {
    size: number
    fly(): void
    sleep(): void
}

class Hummingbird implements Bird { ... }
class Bellbird implements Bird { ... }

尽管您可以使用类型来实现这一点,但Typescript更多地被视为一种面向对象的语言,并且接口在面向对象语言中具有特殊的地位。当您在团队环境中工作或为开源社区做出贡献时,使用界面更容易阅读代码。对于来自其他面向对象语言的新程序员来说也很容易。

官方的Typescript文档还说:

…我们建议尽可能在类型别名上使用接口。

这也表明该类型更适合于创建类型别名,而不是创建类型本身。


声明合并

您可以使用接口的声明合并功能向已声明的接口添加新的财产和方法。这对于第三方库的环境类型声明很有用。当第三方库缺少某些声明时,可以使用相同的名称再次声明接口,并添加新的财产和方法。

例子:

我们可以扩展上述Bird接口以包含新的声明。

interface Bird {
    color: string
    eat(): void
}

就是这样!记住什么时候使用比迷失在两者之间的细微差别中更容易。

2021 3月更新:更新的TypeScript手册(也在nju-clc中提到下面的答案)有一节“接口与类型别名”,解释了区别。


原始答案(2016)

根据(现已存档)TypeScript语言规范:

与总是引入命名对象类型的接口声明不同,类型别名声明可以引入任何类型的名称,包括基元类型、联合类型和交集类型。

该规范还提到:

接口类型与对象类型的类型别名有许多相似之处但是由于接口类型提供了更多的功能通常优选键入别名。例如,接口类型接口点{x: 数量;y: 数量;}可以写成类型别名类型点={x: 数量;y: 数量;};但是,这样做意味着失去以下功能:接口可以在extends或implements子句中命名,但对象类型文本的类型别名不能在TS 2.7之后为true。接口可以有多个合并声明,但对象类型文本的类型别名不能。

文档中指出的关键区别在于,可以重新打开接口以添加新属性,但不能重新打开类型别名以添加新的属性,例如:

这没问题

interface x {
  name: string
}

interface x {
  age: number
}

这将抛出错误Duplicate identifier y

type y = {
  name: string
}

type y = {
  age: number
}

除此之外,接口和类型别名类似。

界面专门设计用于描述对象形状;然而,类型在某种程度上类似于可以用于为任何类型创建新名称的接口。

我们可以说,一个接口可以通过多次声明来扩展;而类型是封闭的。

https://itnext.io/interfaces-vs-types-in-typescript-cf5758211910

索引的差异。

interface MyInterface {
  foobar: string;
}

type MyType = {
  foobar: string;
}

const exampleInterface: MyInterface = { foobar: 'hello world' };
const exampleType: MyType = { foobar: 'hello world' };

let record: Record<string, string> = {};

record = exampleType;      // Compiles
record = exampleInterface; // Index signature is missing

相关问题:类型中缺少索引签名(仅在接口上,而不是在类型别名上)

因此,如果您想为对象编制索引,请考虑这个示例

看看这个问题和这个关于违反利斯科夫原则的问题

评估中的差异

当FirstLevelType为接口时,请查看ExtendeFirst的结果类型

/**
 * When FirstLevelType is interface 
 */

interface FirstLevelType<A, Z> {
    _: "typeCheck";
};

type TestWrapperType<T, U> = FirstLevelType<T, U>;


const a: TestWrapperType<{ cat: string }, { dog: number }> = {
  _: "typeCheck",
};

// {  cat: string; }
type ExtendFirst = typeof a extends FirstLevelType<infer T, infer _>
    ? T
    : "not extended";

当FirstLevelType为类型时,请查看ExtendeFirst的结果类型:


/**
 * When FirstLevelType is type
 */
type FirstLevelType<A, Z>= {
    _: "typeCheck";
};

type TestWrapperType<T, U> = FirstLevelType<T, U>;


const a: TestWrapperType<{ cat: string }, { dog: number }> = {
  _: "typeCheck",
};

// unknown
type ExtendFirst = typeof a extends FirstLevelType<infer T, infer _>
    ? T
    : "not extended";