打字稿:接口与类型

516 typescript

这些语句(接口与类型)之间有什么区别?

interface X {
    a: number
    b: string
}

type X = {
    a: number
    b: string
};
Run Code Online (Sandbox Code Playgroud)

Bin*_*ree 462

根据TypeScript语言规范:

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

规范接着提到:

接口类型与对象类型文字的类型别名有很多相似之处,但由于接口类型提供了更多功能,因此通常首选键入别名.例如,接口类型

interface Point {
    x: number;
    y: number;
}
Run Code Online (Sandbox Code Playgroud)

可以写为类型别名

type Point = {
    x: number;
    y: number;
};
Run Code Online (Sandbox Code Playgroud)

但是,这样做意味着丢失了以下功能:

  • 接口可以在extends或implements子句中命名,但是对象类型文字的类型别名不能.
  • 接口可以有多个合并声明,但对象类型文字的类型别名不能.

  • "多重合并声明"在第二个区别中意味着什么? (85认同)
  • @jrahhali如果你定义接口两次,typescript将它们合并为一个. (50认同)
  • @jrahhali如果你定义类型两次,typescript会给你错误 (28认同)
  • 我相信第一点"扩展或实现"不再是这种情况.类型可以通过`class`进行扩展和实现.这是一个例子http://www.typescriptlang.org/play/#src=type%20MyType%20%3D%20%7B%0D%0A%20%20%20%20name%3A%20string%3B%0D% 0A%20%20%20%20age%3A%20number%0D%0A%7D%3B%0D%0A%0D%0Aclass%20Hello%20implements%20MyType%20%7B%0D%0A%20%20%20% 20public%20name%20%3D%20%22aaa%22%0D%0A%20%20%20%20public%20age%20%3D%20123%3B%0D%0A%7D%0D%0A%0D%0Aconst% 20吨%3A%20MyType%20%3D%20new%20Hello()%3B (18认同)
  • @jrahhali`interface Point {x:number; } interface Point {y:number; }` (14认同)
  • 由于2.7+中的新TypeScript更改,此答案需要更新,请参阅https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c (7认同)
  • 我不敢相信这个用户因为复制粘贴立即找到的内容而获得了 200 多个奖励。这个答案没有给出很好的解释。 (5认同)
  • 没有任何例子就没有多大帮助。 (3认同)
  • @jrahhali我认为[这个SO线程]中的*扩充*场景(http://stackoverflow.com/questions/36782896/in-typescript-what-is-the-difference-between-type-and-interface)回答你的问题题 (2认同)
  • ps:你也可以quazi"扩展"`type`s.请考虑以下事项:`type PartialPerson = {firstName:string; }; 输入Person = PartialPerson&{lastName:string; 但是......虽然,我不推荐它.:) (2认同)
  • 接口不提供的类型联合呢? (2认同)
  • 这个答案已经过时了。 (2认同)
  • @basickarl 这可能是你的观点,但这是我在 Google 上搜索接口和类型之间的区别时的第一件事,它基本上回答了我需要知道的任何内容。这些分数不是根据复杂性而授予的,而是根据有用性而授予的,这个答案肯定是有用的,至少对某些人来说是这样。 (2认同)

jab*_*tta 450

2019年更新


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

1.对象/功能

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

接口

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

interface SetPoint {
  (x: number, y: number): void;
}
Run Code Online (Sandbox Code Playgroud)

输入别名

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

type SetPoint = (x: number, y: number) => void;
Run Code Online (Sandbox Code Playgroud)

2.其他类型

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

// primitive
type Name = string;

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

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];
Run Code Online (Sandbox Code Playgroud)

3.延伸

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

接口扩展接口

interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }
Run Code Online (Sandbox Code Playgroud)

类型别名扩展类型别名

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };
Run Code Online (Sandbox Code Playgroud)

接口扩展类型别名

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }
Run Code Online (Sandbox Code Playgroud)

类型别名扩展接口

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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 };
Run Code Online (Sandbox Code Playgroud)

  • 这提供了有关如何使用类型与接口的更详细信息. (30认同)
  • 基于这篇文章,如果你想使用接口的声明合并(第5点)特性,那么选择类型别名上的接口的*only*理由似乎是.除此之外,它们是等价的(我认为类型别名提供更简洁的语法). (22认同)
  • @Vanquish46 的用例是当一个库提供原始接口而另一个库(例如插件)扩展接口时。 (14认同)
  • 我想知道微软拥有两种不同的语法和如此多的功能重叠背后的推理是什么 (11认同)
  • 有人可以提供一些你为什么想要界面合并的动机吗?这对我来说似乎可能令人困惑。为什么要将接口的定义分散到不同的块上? (9认同)
  • 我总是将接口用于对象类型字面量,否则使用类型会更有意义,而且我认为无论如何都不应使用声明合并,实际上我永远都不会期望接口会在项目的另一个文件中声明额外的属性,类型检查最初是为了使您的生活更轻松,而又不因这种类似于忍者的界面而变得困难:D (5认同)
  • 我想指出的是,这个答案的第 3 部分错误地将交集(“&”)和扩展(“extends”)混为一谈:根据 [this other stackoverflow thread](/sf/answers/3687730161/ 8796651),这两个操作的行为略有不同,因此可能无法按照该答案所暗示的方式互换。 (5认同)
  • 这里是个人意见,但我认为这是最佳实践。我更喜欢始终使用“类型”,并且在处理原型继承链(类和扩展函数)时仅使用“接口” (5认同)
  • 这不能更新并误认为是最佳答案 (4认同)
  • @maxedison我不知道当前的官方文档有多少过时,但它指出:“如果可能的话,您应该始终使用接口而不是类型别名”。[检查](https://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases) (4认同)
  • 如果官方文档已过时,那么在哪里可以确认您提供的信息? (3认同)
  • “键入”可节省您一次点击。即,将鼠标悬停在“类型”上时,vscode 会显示完整的定义,但在接口的情况下不会显示,对于接口,您需要 ctrl+hover 才能查看定义:D (3认同)
  • @jabacchetta,在第3点中也值得一提,当涉及到类型的组合时,最好使用接口来扩展而不是相交类型,因为相交只是递归地合并属性,并且在某些情况下会产生“never”。接口会检测到此类冲突,您将能够及早解决它们。另一个区别是性能,接口被缓存,而类型别名每次都会计算,这些都会影响项目的总编译时间。 (3认同)
  • 类型与接口错误 @CMCDragonkai 来回答你的问题,官方文档在他们的游乐场之一中回答了这个问题:接口和类型:https://www.typescriptlang.org/play/?e=83#example/types-vs-interfaces 行42-46 阅读(以防此链接将来无效) // 也就是说,我们建议您使用接口而不是类型 // 别名。具体来说,因为您会得到更好的错误消息。如果您将鼠标悬停在以下错误上,您可以 // 看到 TypeScript 在使用 Chicken 等界面时如何提供更简洁、更集中的消息。 (3认同)
  • 因此,从根本上说,这是我们真正感到满意的选择的“几乎是个人的”选择?除了一个原因,您可以只使用`type`或`interface`?我仍然对何时应该使用另一种感到困惑。 (2认同)
  • 在实践中,此类部分接口有一个“Partial”接口(https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html),无需引入“PartialInterface” “像示例中那样手动操作。`Partial<Point>` 允许对象只包含一些 `Point` 接口。 (2认同)

Mar*_*ark 93

2021年相关

对于typescrpt版本:4.3.4


TLDR;

我的个人惯例,我在下面描述,是这样的:

总是喜欢interface超过type

何时使用type

  • type在为原始类型(字符串、布尔值、数字、bigint、符号等)定义别名时使用
  • type定义元组类型时使用
  • type定义函数类型时使用
  • type定义联合时使用
  • type在尝试通过组合重载对象类型中的函数时使用
  • 使用type需要采取映射类型的优势,当

何时使用interface

  • interface用于type不需要使用的所有对象类型(见上文)
  • 使用interface时要采取申报合并的advatange。

原始类型

type和之间最容易看到的区别interface是只能type用于为原语别名:

type Nullish = null | undefined;
type Fruit = 'apple' | 'pear' | 'orange';
type Num = number | bigint;
Run Code Online (Sandbox Code Playgroud)

这些示例都无法通过接口实现。

为原始值提供类型别名时,请使用type关键字。

元组类型

元组只能通过type关键字输入:

type row = [colOne: number, colTwo: string];
Run Code Online (Sandbox Code Playgroud)

type为元组提供类型时使用关键字。

函数类型

函数可以通过typeinterface关键字输入:

// via type
type Sum = (x: number, y: number) => number;

// via interface
interface Sum {
  (x: number, y: number): number;
}
Run Code Online (Sandbox Code Playgroud)

由于可以通过任何一种方式实现相同的效果,因此规则将type在这些场景中使用,因为它更容易阅读(并且不那么冗长)。

type在定义函数类型时使用。

联合类型

联合类型只能通过type关键字实现:

type Fruit = 'apple' | 'pear' | 'orange';
type Vegetable = 'broccoli' | 'carrot' | 'lettuce';

// 'apple' | 'pear' | 'orange' | 'broccoli' | 'carrot' | 'lettuce';
type HealthyFoods = Fruit | Vegetable;
Run Code Online (Sandbox Code Playgroud)

定义联合类型时,使用type关键字

对象类型

javascript 中的对象是键/值映射,而“对象类型”是打字稿键入这些键/值映射的方式。二者interfacetype可以为对象提供类型时作为原始问题明确使用。那么什么时候对对象类型使用typevsinterface呢?

交叉与继承

使用类型和组合,我可以做这样的事情:

interface NumLogger { 
    log: (val: number) => void;
}
type StrAndNumLogger = NumLogger & { 
  log: (val: string) => void;
}

const logger: StrAndNumLogger = {
  log: (val: string | number) => console.log(val)
}

logger.log(1)
logger.log('hi')
Run Code Online (Sandbox Code Playgroud)

打字稿非常高兴。如果我尝试使用接口扩展它呢:


interface StrAndNumLogger extends NumLogger { 
    log: (val: string) => void; 
};
Run Code Online (Sandbox Code Playgroud)

的声明StrAndNumLogger给了我一个错误

Interface 'StrAndNumLogger' incorrectly extends interface 'NumLogger'

对于接口,子类型必须与超类型中声明的类型完全匹配,否则 TS 将抛出类似上面的错误。

当尝试重载对象类型中的函数时,最好使用type关键字。

声明合并

打字稿中的接口将它们与类型区分开来的关键方面是它们可以在声明之后使用新功能进行扩展。当您想要扩展从节点模块导出的类型时,会出现此功能的常见用例。例如,@types/jest导出在使用 jest 库时可以使用的类型。但是,jest 还允许jest使用新功能扩展主类型。例如,我可以添加这样的自定义测试:

jest.timedTest = async (testName, wrappedTest, timeout) =>
  test(
    testName,
    async () => {
      const start = Date.now();
      await wrappedTest(mockTrack);
      const end = Date.now();

      console.log(`elapsed time in ms: ${end - start}`);
    },
    timeout
  );
Run Code Online (Sandbox Code Playgroud)

然后我可以像这样使用它:

test.timedTest('this is my custom test', () => {
  expect(true).toBe(true);
});
Run Code Online (Sandbox Code Playgroud)

现在,一旦测试完成,该测试所用的时间将打印到控制台。伟大的!只有一个问题 - typescript 不知道我添加了一个timedTest函数,所以它会在编辑器中抛出一个错误(代码运行良好,但 TS 会生气)。

为了解决这个问题,我需要告诉 TS 在 jest 已经可用的现有类型之上有一个新类型。为此,我可以这样做:

declare namespace jest {
  interface It {
    timedTest: (name: string, fn: (mockTrack: Mock) => any, timeout?: number) => void;
  }
}
Run Code Online (Sandbox Code Playgroud)

由于接口是如何工作的,这种类型的声明将被合并与导出的类型声明@types/jest。所以我不只是重新声明jest.It; 我扩展jest.It了一个新函数,以便 TS 现在知道我的自定义测试函数。

这种类型的事情是不可能的type关键字。如果@types/jest使用type关键字声明它们的类型,我将无法使用我自己的自定义类型扩展这些类型,因此没有什么好方法让 TS 对我的新函数感到满意。这个interface关键字独有的过程称为声明合并

声明合并也可以像这样在本地进行:

interface Person {
  name: string;
}

interface Person {
  age: number;
}

// no error
const person: Person = {
  name: 'Mark',
  age: 25
};
Run Code Online (Sandbox Code Playgroud)

如果我对type关键字做了完全相同的事情,我会得到一个错误,因为类型不能被重新声明/合并。在现实世界中,javascript 对象很像这个interface例子;它们可以在运行时使用新字段动态更新。

因为接口声明可以合并,接口比类型更准确地表示 javascript 对象的动态特性,因此它们应该是首选。

映射对象类型

使用type关键字,我可以利用这样的映射类型

type Fruit = 'apple' | 'orange' | 'banana';

type FruitCount = {
  [key in Fruit]: number;
}

const fruits: FruitCount = {
  apple: 2,
  orange: 3,
  banana: 4
};
Run Code Online (Sandbox Code Playgroud)

这不能通过接口完成:

type Fruit = 'apple' | 'orange' | 'banana';

// ERROR: 
interface FruitCount {
  [key in Fruit]: number;
}
Run Code Online (Sandbox Code Playgroud)

当需要利用映射类型时,使用type关键字

表现

大多数时候,对象类型的简单类型别名的作用与接口非常相似。

interface Foo { prop: string }

type Bar = { prop: string };
Run Code Online (Sandbox Code Playgroud)

但是,一旦您需要组合两个或多个类型,您就可以选择使用接口扩展这些类型,或者将它们交叉在一个类型别名中,这就是差异开始重要的时候。

接口创建一个单一的平面对象类型来检测属性冲突,这通常很重要!另一方面,交叉点只是递归地合并属性,在某些情况下永远不会产生。界面也始终显示得更好,而交叉点的类型别名不能在其他交叉点的一部分中显示。接口之间的类型关系也被缓存,而不是作为一个整体的交集类型。最后一个值得注意的区别是,在检查目标交叉点类型时,在检查“有效”/“扁平”类型之前检查每个成分。

出于这个原因,建议使用接口/扩展扩展类型而不是创建交叉类型。

更多关于打字稿维基

  • 老实说,我认为声明合并是一个非常糟糕的功能,有时会很模糊。对我来说,这通常感觉就像 JavaScript 中的“扩展原型”:您扩展的功能也会泄漏到程序的其他部分。当然它可以很强大并且使事情成为可能,但实际上不应该经常使用/考虑。 (40认同)
  • @paul23 用自己的类型扩展使用的外部库,这非常有帮助且易于使用 (3认同)
  • 如果你正在使用 eslint,你可以设置 `"@typescript-eslint/consistent-type-definitions": [ "error", "interface" ]` 来为你检查这个行为。 (2认同)

Yog*_*ity 78


什么时候使用type


通用转换

使用type时,您要变换多个类型为单个通用型。

例子:

type Nullable<T> = T | null | undefined
type NonNull<T> = T extends (null | undefined) ? never : T
Run Code Online (Sandbox Code Playgroud)

类型别名

我们可以使用type为难以阅读且不方便反复键入的长类型或复杂类型创建别名。

例子:

type Primitive = number | string | boolean | null | undefined
Run Code Online (Sandbox Code Playgroud)

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


类型捕获

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

例子:

const orange = { color: "Orange", vitamin: "C"}
type Fruit = typeof orange
let apple: Fruit
Run Code Online (Sandbox Code Playgroud)

在这里,我们获取 的未知类型orange,将其命名为 a Fruit,然后使用Fruit来创建新的类型安全对象apple


什么时候使用interface


多态性

Aninterface是实现数据形状的合同。使用接口明确表示它旨在实现并用作关于如何使用对象的合同。

例子:

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

class Hummingbird implements Bird { ... }
class Bellbird implements Bird { ... }
Run Code Online (Sandbox Code Playgroud)

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

官方 Typescript文档还说:

......我们建议使用interfacetype可能的情况下别名。

这也表明type更倾向于创建类型别名而不是创建类型本身。


声明合并

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

例子:

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

interface Bird {
    color: string
    eat(): void
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我发现这个答案是解释何时使用其中一种与另一种的最佳答案。 (6认同)
  • 这是最简单也是最好的答案。使用接口来实现接口的用途。对于其他用例,请使用类型别名 (2认同)

Kar*_*ski 68

从TypeScript 3.2开始,以下情况属实:

在此输入图像描述

  • 是的,我的意思是内容的来源,而不是它的表现. (21认同)
  • 该表缺乏任何来源来支持其内容,我不会依赖它。例如,您可以使用具有某些限制的“type”定义递归类型(从 TypeScript 3.7 开始,这些限制也消失了)。接口可以扩展类型。类可以实现类型。此外,以表格屏幕截图的形式呈现数据使得视力受损的人完全无法访问。 (10认同)
  • 您能否提供有关如何生成所提供的表格/图像的更多信息?例如源代码或文档链接 (7认同)
  • @iX3找到了这个内容的来源https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#types-or-interfaces,实际上是这个答案原作者的一条推文 (3认同)
  • 我不相信_class_可以_extend_类型或接口,而且我真的不明白为什么要这么做? (2认同)
  • 避免发布文字图片,而应将实际文字直接添加到您的帖子中。文本图像不容易解析或搜索,并且视障用户无法访问。 (2认同)
  • 据我所知(截至 2021 年 9 月),这张桌子是 Karol Majewski 的原创作品。GitHub 链接引用了该推文,推特对话提到了一本即将出版的书。我希望这本书包含参考文献(例如规范/文档),但到目前为止我还没有看到任何参考文献。 (2认同)

DRe*_*act 50

2024 年更新 -

类型别名和接口非常相似,在许多情况下您可以在它们之间自由选择。接口的几乎所有功能都可以在类型中使用,主要区别在于类型不能重新打开以添加新属性,而接口始终是可扩展的。

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences- Between-type-aliases-and-interfaces


nic*_*ckf 25

https://www.typescriptlang.org/docs/handbook/advanced-types.html

一个区别是接口创建一个在任何地方使用的新名称.类型别名不会创建新名称 - 例如,错误消息将不使用别名.

  • 现在已经过时了,自TypeScript 2.1以来就不再适用了.看到.https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c (17认同)

Prz*_*ski 18

类型示例:

// 为一个对象创建一个树结构。由于缺少交集 (&),您不能对界面做同样的事情

type Tree<T> = T & { parent: Tree<T> };
Run Code Online (Sandbox Code Playgroud)

// type 限制一个变量只能分配几个值。接口没有联合 (|)

type Choise = "A" | "B" | "C";
Run Code Online (Sandbox Code Playgroud)

// 由于类型,您可以通过条件机制声明 NonNullable 类型。

type NonNullable<T> = T extends null | undefined ? never : T;
Run Code Online (Sandbox Code Playgroud)

接口示例:

// 您可以使用 OOP 接口并使用“实现”来定义对象/类骨架

interface IUser {
    user: string;
    password: string;
    login: (user: string, password: string) => boolean;
}

class User implements IUser {
    user = "user1"
    password = "password1"

    login(user: string, password: string) {
        return (user == user && password == password)
    }
}
Run Code Online (Sandbox Code Playgroud)

// 你可以用其他接口扩展接口

    interface IMyObject {
        label: string,
    }

    interface IMyObjectWithSize extends IMyObject{
        size?: number
    }
Run Code Online (Sandbox Code Playgroud)


cap*_*ian 14

索引的差异。

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
Run Code Online (Sandbox Code Playgroud)

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

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

看看这个问题这个关于违反里氏原则的问题

评价差异

ExtendFirst查看when FirstLevelTypeis接口的结果类型

/**
 * 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";
Run Code Online (Sandbox Code Playgroud)

ExtendFirst查看when FirstLevelTypeis a type的结果类型:


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

Run Code Online (Sandbox Code Playgroud)


Ani*_*kwu 11

文档中指出的主要区别是Interface可以重新打开以添加新属性,但Type alias不能重新打开以添加新属性,例如:

还行吧

interface x {
  name: string
}

interface x {
  age: number
}
Run Code Online (Sandbox Code Playgroud)

这会抛出错误Duplicate identifier y

type y = {
  name: string
}

type y = {
  age: number
}
Run Code Online (Sandbox Code Playgroud)

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


Yil*_*maz 9

在打字稿中,建议使用“接口”而不是“类型”。

\n\n

别名非常有用,尤其是在处理泛型类型时。

\n
    \n
  • Declaration Merging:您可以合并接口,但不能合并类型。

    \n
    interface Person {\n    name: string;\n  }\n\n  interface Person {\n    age: number;\n  }\n  // we have to provide properties in both Person\n  const me: Person = {\n    name: "Yilmaz",\n    age: 30\n  };\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • 函数式编程用户使用“类型”,面向对象编程用户选择“接口”

    \n
  • \n
  • 您可以\xe2\x80\x99t 在接口上但在类型中计算或计算属性。

    \n
     type Fullname = "firstname" | "lastname"\n\n  type  Person= {\n     [key in Fullname]: string\n  }\n\n  const me: Person = {\n     firstname: "Yilmaz",\n     lastname: "Bingol"\n  }\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n


qin*_*127 9

在我的日常开发中,当我不知道该选择哪个时,我会使用这个备忘单。

欲了解更多信息,您可以阅读我的博客:https://medium.com/@magenta2127/use-which-interface-or-type-alias-in-typescript-bdfaf2e882ae


dmu*_*dro 7

除了已经提供的出色答案之外,在扩展类型与接口方面还存在明显的差异。我最近遇到了一些界面无法完成这项工作的情况:

  1. 无法使用接口扩展联合类型
  2. 无法扩展通用接口


Jer*_*yal 6

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

您可以在类型中使用 union

type Name = string | { FullName: string };

const myName = "Jon"; // works fine

const myFullName: Name = {
  FullName: "Jon Doe", //also works fine
};
Run Code Online (Sandbox Code Playgroud)

迭代类型中的联合属性

type Keys = "firstName" | "lastName";

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

const myName: Name = {
  firstName: "jon",
  lastName: "doe",
};
Run Code Online (Sandbox Code Playgroud)

类型中的交集(但是,Interface with 中也支持extends

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

type Address = {
  city: string;
};

const person: Name & Address = {
  firstName: "jon",
  lastName: "doe",
  city: "scranton",
};
Run Code Online (Sandbox Code Playgroud)

也不type是后来引入的interface,根据最新版本的 TStype可以做*几乎所有可以做的事情,interface还有更多!


*除了Declaration merging个人意见:不支持类型是好的,因为它可能导致代码不一致


Wil*_*een 5

接口与类型

接口和类型用于描述对象和基元的类型。接口和类型通常可以互换使用,并且通常提供类似的功能。通常是程序员根据自己的喜好进行选择。

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

以下是接口和类型之间的 2 个差异的示例:

// 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 
Run Code Online (Sandbox Code Playgroud)


小智 5

Typescript 手册给出了答案:关键区别在于,不能重新打开类型以添加​​新属性与始终可扩展的接口。

链接:https : //www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases