Leecason

vuePress-theme-reco Leecason    2018 - 2020
Leecason Leecason

Choose mode

  • dark
  • auto
  • light
主页
分类
  • CSS
  • FrontEnd
  • GraphQL
  • JavaScript
  • TypeScript
  • Vue
  • Webpack
  • 其它
  • 数据结构与算法
  • 浏览器相关
标签
时间线
GitHub
author-avatar

Leecason

80

Article

61

Tag

主页
分类
  • CSS
  • FrontEnd
  • GraphQL
  • JavaScript
  • TypeScript
  • Vue
  • Webpack
  • 其它
  • 数据结构与算法
  • 浏览器相关
标签
时间线
GitHub

TypeScript: Type vs Interface

vuePress-theme-reco Leecason    2018 - 2020

TypeScript: Type vs Interface

Leecason 2020-02-21

比较 Type 与 Interface 在使用时的相似和不同。

# 相似处

# 定义

interface IAnimal {
  name: string;
}

type Animal = {
  name: string;
};
1
2
3
4
5
6
7

# 泛型

interface IAnimal<P = string> {
  name: P;
}

type Animal<P = string> = {
  name: P;
};
1
2
3
4
5
6
7

# 合并

type Robot = {
  power: number;
};

interface IRobot {
  name: string;
}

interface IRoboAnimal1 extends IAnimal, IRobot {}
interface IRoboAnimal2 extends IAnimal, Robot {}
interface IRoboAnimal3 extends Animal, IRobot {}
interface IRoboAnimal4 extends Animal, Robot {}

type RoboAnimal1 = Animal & Robot;
type RoboAnimal2 = Animal & IRobot;
type RoboAnimal3 = IAnimal & Robot;
type RoboAnimal4 = IAnimal & IRobot;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 实现

class Dog implements IAnimal {
  name: string = "good dog";
}

class Cat implements Animal {
  name: string = "Where is my food, human?";
}
1
2
3
4
5
6
7

# 继承类

class Control {
  private state: any;
}

interface ISelectableControl extends Control {
  select(): void;
}

type SelectableControl = Control & {
  select: () => void;
};
1
2
3
4
5
6
7
8
9
10
11

# 定义函数

type Bark = (x: Animal) => void;

interface iBark {
  (x: Animal): void;
}

// 使用泛型
type Bark = <P = Animal>(x: P) => void;

interface iBark {
  <P = Animal>(x: P): void;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 递归定义

type Tree<P> = {
  node: P;
  leafs: Tree<P>[];
};

interface ITree<P> {
  node: P;
  leafs: ITree<P>[];
}
1
2
3
4
5
6
7
8
9

# 精确(形状保持一致)

type Close = { a: string };
const x: Close = { a: "a", b: "b", c: "c" };
// Type '{ a: string; b: string; c: string; }' is not assignable to type 'Close'.

interface IClose {
  a: string;
}
const y: IClose = { a: "a", b: "b", c: "c" };
// Type '{ a: string; b: string; c: string; }' is not assignable to type 'IClose'.
1
2
3
4
5
6
7
8
9

# 任意属性

type StringRecord = {
  [index: string]: number;
};

interface IStringRecord {
  [index: string]: number;
}
1
2
3
4
5
6
7

# 不同处

# 原始数据类型

你只能用 Type 来给原始数据类型取别名。

type NewNumber = number;

interface INewNumber extends number {}
// 'number' only refers to a type, but is being used as a value here.

// 可以继承 Number
interface INewNumber extends Number {}
// 但是别忘了 1 instanceof Number === false
1
2
3
4
5
6
7
8

# 元祖

不能用 Interface 声明元祖。

type Tuple = [number, number];

interface ITuple {
  0: number;
  1: number;
}

[1, 2, 3] as Tuple; // Conversion of type '[number, number, number]' to type '[number, number]' may be a mistake

[1, 2, 3] as ITuple; // Ok
1
2
3
4
5
6
7
8
9
10

# 联合类型

只有 Type 能使用联合类型。

type DomesticAnimals = { type: "Dog" } | { type: "Cat" };
1

联合类型不能与 extends 一起使用。

interface IDomesticAnimals extends DomesticAnimals {}
// An interface can only extend an object type or intersection of object types with statically known members
1
2

# new

你可以声明 new 的类型

interface IClassyAnimal {
  new (name: string);
}
1
2
3

但是不会像你预期那样工作

class Parrot implements IClassyAnimal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
// Class 'Parrot' incorrectly implements interface 'IClassyAnimal'.
//  Type 'Parrot' provides no match for the signature 'new (name: string): void'.
1
2
3
4
5
6
7
8

constructor 也是如此

interface IClassyAnimal {
  constructor(name: string): void;
}

class Parrot implements IClassyAnimal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
// Class 'Parrot' incorrectly implements interface 'IClassyAnimal'.
//  Types of property 'constructor' are incompatible.
//    Type 'Function' is not assignable to type '(name: string) => void'.
//      Type 'Function' provides no match for the signature '(name: string): void'.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 在作用域中声明多次

每个作用域内只能声明一次 Type。

type Once = { a: string };
type Once = { b: string };
// Duplicate identifier 'Once'.
1
2
3

可以声明多次 Interface(结果会合并所有的声明)。

interface IOnce {
  a: string;
}
interface IOnce {
  b: string;
}
1
2
3
4
5
6

# 实用类型

大多数情况下,你会使用 Type 而不是 Interface 来创建实用类型。

export type NonUndefined<A> = A extends undefined ? never : A;
1

# 结尾

在早期的TS版本中,大家习惯了 Interface。但在最新版本的TS,似乎 Type 有着更强大的能力。在 TS 中有很多细微的差别,有些例子可能适合 Interface,但有些情况可能 Type 会更合适,需要自己多做总结。

# 参考

  • DEV -- TypeScript: type vs interface
  • EDUCBA -- TypeScript Type vs Interface
  • MEDIUM -- Interface vs Type alias in TypeScript 2.7