interface和type的区别

185 阅读3分钟

区别

1. Objecs/Functions(对象和方法)

两个都可以用来定义对象或者函数签名。但是语法略有不同。

interface

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

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

Type alias

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

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

2.Other Types

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

// 基本类型 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 extends interface

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

Type alias extends type alias

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

Interface extends type alias

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

Type alias extends interface

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

4.Implements

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

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

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

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

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

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

// FIXME: 不能实现联合类型
class SomePartialPoint implements PartialPoint {
  x = 1;
  y = 2;
}

5.Declaration merging(声明合并)

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

// 这两个声明会变成:
// interface Point { x: number; y: number; }
interface Point { x: number; }
interface Point { y: number; }

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

使用方式

一般情况下:选择接口,少用类型别名(个人习惯)。

什么时候使用类型别名(type)?

  • 为基本数据类型(字符串、布尔值、数字、bigint、符号等)定义别名时使用type
  • 定义元组类型时使用type
  • 定义函数类型时使用type
  • 定义联合时使用type
  • 当试图通过复合重载对象类型中的函数时,请使用type
  • 当需要利用映射类型时使用type

基本数据类型

只有type能为基本数据类型设置别名,interface是做不到的。

type Nullish = null | undefined;
type Fruit = 'apple' | 'pear' | 'orange';
type Num = number | bigint;

元组类型(Tuple types)

元组类型也只可以用type来定义:

type row = [colOne: number, colTwo: string];

函数类型

函数既可以用type定义,也可以用interface定义,个人感觉type比较方便一点,方便定义和阅读:

// 通过type定义
type Sum = (x: number, y: number) => number;

// 通过interface定义
interface Sum {
  (x: number, y: number): number;
}

联合类型(union types)

联合类型只能用type定义:

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

对象类型(Object types)

在为对象提供类型时,接口和类型都可以使用,什么时候使用interface什么时候使用type呢?

重载对象类型中的函数时,使用type

先来看一个例子:

interface NumLogger {
  log: (val: number) => void;
}

// 或者这样写也是可以的
// type NumLogger = {
//   log: (val: number) => void;
// };

type StrAndNumLogger = NumLogger & {
  log: (vals: string) => void;
};

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

logger.log("dasd");
logger.log(12);

对象中的log方法重载了,此时传入的参数可以是字符串也可以是数字

如果使用interface重载:

interface NumLogger {
  log: (val: number) => void;

}
// 这里的StrAndNumLogger会报错
/**
 * 接口'StrAndNumLogger'不正确地扩展接口'NumLogger'。
 * 属性“log”的类型不兼容。
 * Type '(val: string) => void'不能赋值给Type '(val: number) => void'。
 * 参数'val'和'val'的类型不兼容。
 * 类型“number”不能赋值给类型“string”。
 */
interface StrAndNumLogger extends NumLogger { 
    log: (val: string) => void; 
};

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

logger.log("dasd");
logger.log(12);

从报错信息可以看出,子可以重写父函数中的函数体,但是函数的类型是不能改变的。

什么时候使用接口(interface)?

  • 对于不需要使用type的所有对象类型使用interface
  • 当利用声明合并时,请使用interface

参考文档