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

interface X {
    a: number
    b: string
}

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

当前回答

其他答案很棒!Type可以做但Interface不能做的其他事情很少

可以在类型中使用联合

type Name = string | { FullName: string };

const myName = "Jon"; // works fine

const myFullName: Name = {
  FullName: "Jon Doe", //also works fine
};

在类型中迭代联合财产

type Keys = "firstName" | "lastName";

type Name = {
  [key in Keys]: string;
};

const myName: Name = {
  firstName: "jon",
  lastName: "doe",
};

类型中的交集(但是,在带有扩展的接口中也支持)

type Name = {
  firstName: string;
  lastName: string;
};

type Address = {
  city: string;
};

const person: Name & Address = {
  firstName: "jon",
  lastName: "doe",
  city: "scranton",
};

与接口相比,这种类型也不是后来才引入的,根据最新发布的TS类型,它几乎可以做任何接口可以做的事情,而且更多!


*除了声明合并(个人观点:很好,它在类型中不受支持,因为它可能导致代码不一致)

其他回答

2019年更新


目前的答案和官方文件已经过时。对于那些新接触TypeScript的人来说,如果没有例子,使用的术语就不清楚了。以下是最新差异列表。

1.对象/功能

两者都可以用来描述对象或函数签名的形状。但语法不同。

界面

interface Point {
  x: number;
  y: number;
}

interface SetPoint {
  (x: number, y: number): void;
}

类型别名

type Point = {
  x: number;
  y: number;
};

type SetPoint = (x: number, y: number) => void;

2.其他类型

与接口不同,类型别名也可以用于其他类型,如基元、联合和元组。

// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];

3.延伸

两者都可以扩展,但语法也不同。此外,请注意,接口和类型别名不是互斥的。接口可以扩展类型别名,反之亦然。

接口扩展接口

interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }

类型别名扩展类型别名

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

接口扩展类型别名

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }

类型别名扩展接口

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

4.工具

类可以以相同的方式实现接口或类型别名。但是请注意,类和接口被视为静态蓝图。因此,它们不能实现/扩展命名联合类型的类型别名。

interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x = 1;
  y = 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x = 1;
  y = 2;
}

type PartialPoint = { x: number; } | { y: number; };

// FIXME: can not implement a union type
class SomePartialPoint implements PartialPoint {
  x = 1;
  y = 2;
}

5.申报合并

与类型别名不同,接口可以定义多次,并将被视为单个接口(合并所有声明的成员)。

// These two declarations become:
// interface Point { x: number; y: number; }
interface Point { x: number; }
interface Point { y: number; }

const point: Point = { x: 1, y: 2 };

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


原始答案(2016)

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

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

该规范还提到:

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

索引的差异。

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";

接口与类型

接口和类型用于描述对象和原语的类型。接口和类型通常可以互换使用,并且通常提供类似的功能。通常由程序员选择自己的偏好。

然而,接口只能描述创建这些对象的对象和类。因此,必须使用类型来描述字符串和数字等原语。

下面是接口和类型之间的两个区别的示例:

// 1. Declaration merging (interface only)

// This is an extern dependency which we import an object of
interface externDependency { x: number, y: number; }
// When we import it, we might want to extend the interface, e.g. z:number
// We can use declaration merging to define the interface multiple times
// The declarations will be merged and become a single interface
interface externDependency { z: number; }
const dependency: externDependency = {x:1, y:2, z:3}

// 2. union types with primitives (type only)

type foo = {x:number}
type bar = { y: number }
type baz = string | boolean;

type foobarbaz = foo | bar | baz; // either foo, bar, or baz type

// instances of type foobarbaz can be objects (foo, bar) or primitives (baz)
const instance1: foobarbaz = {y:1} 
const instance2: foobarbaz = {x:1} 
const instance3: foobarbaz = true 

根据我最近看到或参与的所有讨论,类型和接口之间的主要区别在于接口可以扩展,而类型不能扩展。

此外,如果您两次声明一个接口,它们将被合并为一个接口。你不能用打字。