「TypeScript 技巧」:Interface or Type

337 阅读5分钟

摘要

Typescript 的核心原则之一是对值所具有的结构进行类型检查,接口(interface) 和类型别名(type)的作用就是为代码定义契约。然而 interface 和 type 的区别是什么,在开发中使用哪个,这些问题困扰着开发者。本文通过代码示例,首先对网上常见的 3 个错误观点进行纠正,然后总结了两者的 4 个不同之处。最后给出了使用 interface 和 type的建议。为不是使用 TypeScript 编写的库编写第 3 方环境类型定义时 或 我们的库是用 TypeScript 编写的并且环境类型定义是自动生成的使用 interface,在 React Props 和 State 建议使用 type

在线运行代码:codesandbox.io/s/suspiciou…

Introduce

TypeScript 允许类型与使用它们的变量分开定义,InterfaceType 允许不同的变量 / 对象之前公用。TypeScript 官网指出,“类型别名可以有点像接口,但是有一些细微的差别”。

错误观点

1. interface 创建新名称,type 不会创建新的名称——不会抛出错误

创建一个接口 pointInterface 和 一个 类型 pointType,声明 point_interface 和 point_type。未声明对象中 y 属性。 TypeScript 基础使用链接

interface PointInterface {
  x: number;
  y: number;
}
type PointType = {
  x: number;
  y: number;
};
​
const point_interface: PointInterface = { x: 1 };
const point_type: PointType = { x: 1 };

ts 给出了相同的报错

1.gif

// TS Error:
​
Property 'y' is missing in type '{ x: number; }' but required in type 'pointInterface'.ts(2741)
errorPoints.ts(3, 3): 'y' is declared here.
​
Property 'y' is missing in type '{ x: number; }' but required in type 'pointType'.ts(2741)
errorPoints.ts(8, 3): 'y' is declared here.

2. type 不支持扩展(extends) 或 实现(implemented)

// case2: type supports extends
interface ThreeDimensions extends PointType {
  z: number;
}
// case2: type can be used by implements
class Rectangle implements PointType {
  x = 2;
  y = 3;
}

由 type 扩展来的接口也可以作为类的约束:

// case2: interface extends from type
class RectanglePrism implements ThreeDimensions {
  x = 2;
  y = 3;
  z = 4;
}

type 和 interface 支持混合使用作为类的约束:

interface Shape {
  area(): number;
}
interface Perimeter {
  perimiter(): number;
}
class RectangleDeep implements PointType, Shape, Perimeter {
  x = 2;
  y = 3;
  area() {
    return this.x * this.y;
  }
  perimiter() {
    return 2 * (this.x + this.y);
  }
}

3. type 不支持从其他 type 中扩展

type 支持通过交集运算符 & 进行类型别名扩展

type RectangleDeepType = PointType & Shape & Perimeter
class RectangleDeep implements RectangleDeepType {
  x = 2;
  y = 3;
  area() {
    return this.x * this.y;
  }
  perimiter() {
    return 2 * (this.x + this.y);
  }
}

Interface & type 区别

1. type 适用于所有类型,interface 仅适用于对象

interface 受限于声明方式,仅适用于对象类型。

// case1: type use basic type
type FirstName = string;
const firstName: FirstName = "firstName";

2. 如果在 type 定义中使用联合运算符(|),则不能在具有类型别名的类上使用 implements

type PointType = {
  x: number;
  y: number;
};
interface Shape {
  area(): number;
}
interface Perimeter {
  perimiter(): number;
}
type RectangleDeepType = (PointType | Shape) & Perimeter;
class RectangleDeep implements RectangleDeepType {
  x = 2;
  y = 3;
  area() {
    return this.x * this.y;
  }
  perimiter() {
    return 2 * (this.x + this.y);
  }
}

image.png

这是完全符合逻辑的。 类可以视为蓝图,不能实现一个或另一个形状结构。

3. 如果在 extends 定义中使用联合运算符,则不能在具有 type 的接口上使用扩展

// case 3: interface extends cannot support type |
type ShapeOrPermiter = Shape | Perimeter;
interface RectangleShape extends ShapeOrPermiter {}

同样,与 2 类似,interface 是一个静态蓝图——它不能以一种或另一种形式存在,因此不能通过 union 类型合并来扩展。

image.png

4. 声明合并不适用于 type

多次定义同一个接口,它的定义将合并为一个:

// case 4: merge
interface Table {
  price: number;
}
interface Table {
  size: number;
}
const table: Table = {
  size: 100,
  price: 200
};
​
type Pen = {
  price: number;
};
type Pen = {
  size: number;
};

这不适用于类型别名,因为类型是一个唯一的类型实体(对于全局或模块范围):

image.png


何时使用 interface

通过 interface 合并声明非常重要,当我们为不是使用 TypeScript 编写的库编写第 3 方环境类型定义时,如果缺少某些定义,使用方可以选择扩展它们。

如果我们的库是用 TypeScript 编写的并且环境类型定义是自动生成的,则同样适用。

这是唯一的用例,应该始终使用 interface 而不是 type!

何时使用 type

在React Props 和 State 定义中,建议使用类型别名:

  • 写成 type Props = {} 更短
  • 使用语法是一致的(通常没有 interface 和 type 混用的必要)
  • 你的公共组件 Props/State 实现不能被修改,因此,组件的消费者永远不需要利用接口声明合并。
// BAD
interface Props extends OwnProps, InjectedProps, StoreProps {}
type OwnProps = {...}
type StoreProps = {...}
// GOOD
type Props = OwnProps & InjectedProps & StoreProps
type OwnProps = {...}
type StoreProps = {...}

总结

本文总结了 TypeScript 中接口(interface) 和类型别名(type)的作用和不同之处。两者的作用都是进行类型检查。首先对常见的错误观点进行纠正,interface 和 type 都会创建一个新的名称;type 支持扩展和实现;type 支持从其他 type 中扩展。其次梳理了两者的区别:type可以作用于基本数据类型,interface 只能作用于对象;使用了联合运算符(|) 的 type 不能被 class 实现;使用了联合运算符(|) 的 type 不能被 interface extends;声明合并不适用于 type。最后我们总结了,为不是使用 TypeScript 编写的库编写第 3 方环境类型定义时 或 我们的库是用 TypeScript 编写的并且环境类型定义是自动生成的使用 interface,在 React Props 和 State 建议使用 type。

在线运行代码:codesandbox.io/s/suspiciou…

引用

[1​]  typescript.bootcss.com/interfaces.…

[2]  medium.com/@martin_hot…

[3]  juejin.cn/post/684490…

[4]  www.geeksforgeeks.org/what-are-ty…

[5]  www.typescriptlang.org/docs/handbo…

[​6]  www.w3schools.com/typescript/…

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情