TS 中的接口

刚接触 typescript 的同学可能会对接口这个概念感到陌生,毕竟 js 里可没有这么个玩意儿。本篇文章就来介绍一下,到底什么是接口?它又能起到什么作用?

基础用法

接口是对象的状态和行为的抽象,使用接口来对一个对象的属性和方法的类型进行声明。比如,我们需要定义个歌手对象,其有以下 4 种属性:

  1. id:字符串类型,只读的,必需的;
  2. name:字符串类型,必需的;
  3. age:数字类型,必需的;
  4. gender:字符串类型,可选的。

那么接口就可以这么写: 使用关键字 interface 定义接口,写法同定义 class 类相似,接口名后不用跟括号,我习惯接口名以 I 开头,{} 里的内容不需要用逗号分割:

typescript 复制代码
interface ISinger {
  readonly id: string // readonly 写在属性名前,代表只读属性,赋值后不能修改
  name: string
  age: number
  gender?: string // 属性名后跟个问号代表是可选属性
  sing(): void // 定义方法
  run: () => void // 也可以写成这样定义方法
}

在定义一个对象的时候,类型就可以是接口 ISinger

typescript 复制代码
const Jay: ISinger = {
  id: 'z1234',
  name: 'Jay',
  age: 22,
  sing() { },
  run() { }
}

除了可选属性,其它属性的必须要进行定义,不能多,也不能少。但是,如果是下面这种写法,则又允许将拥有 IPerson 接口中不存在的属性的对象 temObj 赋值给 Jay

typescript 复制代码
interface IPerson {
  name: string
  age: number
}

const temObj = {
  name: 'Jay',
  age: 22,
  height: 12 // IPerson 中不存在的属性
}

const Jay: IPerson = temObj

这种现象的原因,与下面介绍的 freshness 检测规则有关:

Freshness

在对字面量进行赋值时,ts 的类型检测有个叫做 freshness(字面意思为"新鲜") 的规则。比如下例中 singer 类型为 singerType,而直接赋值的字面量对象里多了个 sing 方法,就会报错:

但如果我们把字面量对象先赋值给 temp ,在把 temp 赋值给 singer,就不会报错:

typescript 复制代码
type singerType = {
  name: string
  age: number
}
const temp = {
  name: 'Jay',
  age: 18,
  sing() { }
}

const singer: singerType = temp

这是因为 ts 在做类型检测时,会默认把 temp 中多出来的 sing 属性去除,所以,如果我们之后调用 sing 方法,类型检测也会报错:

类对接口的实现

implements

类除了可以继承类,也可以实现(implements)接口:

typescript 复制代码
interface ISinger {
  sing(): void // 该方法没有任何的实现
}

class Singer implements ISinger {
  sing() {
    console.log('作为一名歌手一年出张专辑不过分吧~')
  }
}

const Jay = new Singer()
Jay.sing()

接口 ISinger 定义了 sing 方法,类 Singer 实现了 ISinger,则 Singer 类中也必须定义 sing 方法。

一个类可以实现多个接口

一个类只可以继承自一个类,但可以实现多个接口,接口之间用 , 分割。每个接口中的内容都要真正实现:

typescript 复制代码
interface ISinger {
  sing(): void
}
interface IFatPersong {
  eat(): void
}

class Singer implements ISinger, IFatPersong {
  sing() {
    console.log('作为一名歌手一年出张专辑不过分吧~')
  }
  eat() {
    console.log('就知道喝奶茶')
  }
}

接口继承接口

和类一样,接口也可以相互继承,一个接口可以继承多个接口。上面一个类可以实现多个接口例子也可以用下面这种方式:

typescript 复制代码
interface IFatSinger extends ISinger, IFatPersong {}

// 直接实现上面这个类
class Singer implements IFatSinger {
  sing() {}
  eat() {}
}

注意:接口之间是继承(extends)关系,类和接口之间是实现(implements)关系。

和 type 对比

《TS 中的类型》中介绍了定义类型别名的 type, 它和 interface 都可以定义对象的类型,它们有什么区别呢?

  • 写法的区别,type 有用到 =,是赋值式的写法;而 interface 的定义没有用到 =,是声明式的写法:
typescript 复制代码
type PersonType = {
  name: string
  age?: number
}

interface IPerson {
  name: string
  age?: number
}
  • type 可以定义的类型范围比 interface 大,一般定义函数或者联合类型时,使用 type,而 interface 用于定义对象,并且官方文档有下面这么一句话 :

在大多数情况下,你可以根据个人喜好进行选择,TypeScript 会告诉你它是否需要其他类型的声明。如果您想要启发式方法,可以使用 interface 直到你需要使用 type 中的功能。

  • interface 可以重复定义,而 type 不可以。多次定义的 ISinger 接口的属性会合并,所以 Singer 需要同时有 singeat 方法:
typescript 复制代码
interface ISinger {
  sing(): void
}
interface ISinger {
  eat(): void
}

class Singer implements ISinger {
  sing() { }
  eat() { }
}
  • 接口可以被类实现,并且支持继承,而 type 则不行。

索引签名(Index Signatures)

当我们想定义一个属性名均为数字类型的对象 obj1,可以设置接口的 key 的类型,当然这里第 2 行的 key 只是个形参,是自定义的,类型可以是stringnumbersymbol

typescript 复制代码
interface IObj1 {
  [key: number]: string
}
const obj1: IObj1 = {
  0: 'Jay',
  1: 'Join',
  2: 'Eson'
}

interface IObj2 {
  [key: symbol]: string
}
const sym: symbol = Symbol()
const obj2: IObj2 = {
  [sym]: 'Jay',
}

obj1 的类型被注释为 IObj1 后,obj1 的属性名就只能是数字了。

另外还有一个细节,索引类型为 number 的值的类型,必须是索引类型为 string 的值的子类型:

typescript 复制代码
interface IObj {
  [key1: string]: string | number
  [key2: number]: string
}

如果像下面这样,就会报错:

因为在 js 中,使用数字进行索引时,实际上会在索引到对象之前将数字转换成字符串,即 arr[1]arr['1'] 是一样的。

同理,如果定义了索引类型为 string 的索引签名,又定义了其它属性,那么其它属性的值的类型,也必须是索引类型为 string 的值的类型的子类型:

typescript 复制代码
interface IObj {
  [key: string]: string | number
  name: string
}

相关推荐
Zhencode9 分钟前
Vue3核心运行时之runtime-core
前端·javascript·vue.js
木斯佳13 分钟前
前端八股文面经大全:腾讯WXG技术架构前端面试(2025-11-19)·面经深度解析
前端·面试·架构
感性的程序员小王23 分钟前
HTTPS页面请求HTTP接口失败?一文讲透Mixed Content
前端·后端
用户6000718191041 分钟前
【翻译】我竟渐渐迷上了生成器的设计巧思
前端
Wect44 分钟前
LeetCode 104. 二叉树的最大深度:解题思路+代码解析
前端·算法·typescript
千寻girling1 小时前
面试官 : “ 请说一下 JS 的常见的数组 和 字符串方法有哪些 ? ”
前端·javascript·面试
Wect1 小时前
LeetCode 100. 相同的树:两种解法(递归+迭代)详解
前端·算法·typescript
我是伪码农1 小时前
Vue 大事件管理系统
前端·javascript·vue.js
henry1010101 小时前
DeepSeek生成的网页版小游戏 - 冰壶
前端·javascript·css·html5
木斯佳1 小时前
前端八股文面经大全:腾讯云前端实习一面(2025-12-26)·面经深度解析
前端·状态模式·腾讯云