TypeScript 中 Interface 与 Type 的区别?该用哪个比较好?

点击上方 三分钟学前端,关注公众号

回复交流,加入前端编程面试算法每日一题群

面试官也在看的前端面试资料

接口 与 类型别名 的异同点

相同点

1. 都可以描述对象或函数

// 接口
interface Sister {
  name: string;
  age: number;
}

interface SetSister {
  (name: string, age: number): void;
}

// 类型别名
type Sister = {
  name: string;
  age: number;
};

type SetSister = (name: string, age: number) => void;

2. 都可以扩展

interface 和 type 可以混合扩展,也就是说 interface 可以扩展 typetype 也可以扩展 interface

但需要注意的是,接口的扩展是继承( extends )。类型别名的扩展就是交叉类型(通过 & 实现)

// 接口
interface SisterAn {
    name: string;
}

// 类型别名
type SisterRan = {
    age: number;
}
// 接口扩展接口
interface Sister extends SisterAn {
    age: number;
}
// 类型别名扩展类型别名
type SisterPro = SisterRan & {
    name: string;
}
// 接口扩展类型别名
interface Sister extends SisterRan {
    name: string;
}
// 类型别名扩展接口
type SisterPro = SisterAn & {
    age: number;
}

区别

官方 中这样介绍两者的区别:

Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.

意思就是说几乎接口的所有特性都可以通过类型别名来实现,主要区别在于:

1. 不同的声明范围

与接口不同,可以为任意的类型创建类型别名

类型别名的右边可以是任何类型,包括基本类型、元祖、类型表达式( &| 等);而在接口声明中,右边必须为变量结构。例如,下面的类型别名就不能转换成接口

type Name = string
type Text = string | { text: string };
type Coordinates = [number, number];

2. 不同的扩展形式

接口是通过继承的方式来扩展,类型别名是通过 & 来扩展

// 接口扩展
interface SisterAn {
    name: string;
}
interface Sister extends SisterAn {
    age: number;
}

// 类型别名扩展
type SisterRan = {
    age: number;
}
type SisterPro = SisterRan & {
    name: string;
}

这里需要注意的是,接口扩展时,typescript 会检查扩展的接口是否可以赋值给被扩展的接口

// 接口扩展
interface SisterAn {
    name: string;
    age: string
}

interface Sister extends SisterAn {
    name: string;
    age: number;
}
// 报错:
//  Interface 'Sister' incorrectly extends interface 'SisterAn'.
//  Types of property 'age' are incompatible.
//  Type 'number' is not assignable to type 'string'

但使用交集类型时就不会出现这种情况

// 类型别名扩展
type SisterRan = {
 name: string;
    age: string;
}
type SisterPro = SisterRan & {
    name: string;
    age: number;
}

类型别名扩展时,typescript 将尽其所能把扩展和被扩展的类型组合在一起,而不会抛出编译时错误

3. 不同的重复定义表现形式

接口可以定义多次,多次的声明会自动合并

interface Sister {
    name: string;
}
interface Sister {
    age: number;
}

const sisterAn: Sister = {
    name: 'sisterAn'
} 
// 报错:Property 'age' is missing in type '{ name: string; }' but required in type 'Sister'

const sisterRan: Sister = {
    name: 'sisterRan', 
    age: 12
}
// 正确

但是类型别名如果定义多次,会报错

type Sister = { // Duplicate identifier 'Sister'
    name: string;
}

type Sister = { // Duplicate identifier 'Sister'
    age: number;
}

如何选择 Interface 、 Type

虽然 官方 中说几乎接口的所有特性都可以通过类型别名来实现,但建议优先选择接口,接口满足不了再使用类型别名,在 typescript 官网 Preferring Interfaces Over Interps 有说明,具体内容如下:

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

interface Foo { prop: string }

type Bar = { prop: string };

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

  • 接口创建一个单一的平面对象类型来检测属性冲突,这通常很重要!  而交叉类型只是递归的进行属性合并,在某种情况下可能产生 never 类型

  • 接口也始终显示得更好,而交叉类型做为其他交叉类型的一部分时,直观上表现不出来,还是会认为是不同基本类型的组合。

  • 接口之间的类型关系会被缓存,而交叉类型会被看成组合起来的一个整体。

  • 最后一个值得注意的区别是,在检查到目标类型之前会先检查每一个组分。

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

- type Foo = Bar & Baz & {
-     someProp: string;
- }
+ interface Foo extends Bar, Baz {
+     someProp: string;
+ }

简单的说,接口更加符合 JavaScript 对象的工作方式,简单的说明下,当出现属性冲突时:

// 接口扩展
interface Sister {
    sex: number;
}

interface SisterAn extends Sister {
    sex: string;
}
// index.ts(5,11): error TS2430: Interface 'SisterAn' incorrectly extends interface 'Sister'.
//  Types of property 'sex' are incompatible.
//    Type 'string' is not assignable to type 'number'.
// 交叉类型
type Sister1 = {
    sex: number;
}

type Sister2 = {
    sex: string;
}

type SisterAn = Sister1 & Sister2;
// 不报错,此时的 SisterAn 是一个'number & string'类型,也就是 never

来源:https://github.com/Advanced-Frontend/Daily-Interview-Question

最后

欢迎关注「三分钟学前端」,回复「交流」自动加入前端三分钟进阶群,每日一道编程算法面试题(含解答),助力你成为更优秀的前端开发!

号内回复:

网络」,自动获取三分钟学前端网络篇小书(90+页)

JS」,自动获取三分钟学前端 JS 篇小书(120+页)

算法」,自动获取 github 2.9k+ 的前端算法小书

面试」,自动获取 github 23.2k+ 的前端面试小书

简历」,自动获取程序员系列的 120 套模版

》》面试官也在看的前端面试资料《《

“在看和转发”就是最大的

微信扫码订阅
UP更新不错过~
关注
  • 0
    点赞
  • 2
    收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 1
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值