> 文章列表 > Typescript - 通俗易懂的 interface 接口,创建接口 / 基础使用 / 可选属性 / 只读属性 / 任意属性(详细教程)

Typescript - 通俗易懂的 interface 接口,创建接口 / 基础使用 / 可选属性 / 只读属性 / 任意属性(详细教程)

Typescript - 通俗易懂的 interface 接口,创建接口 / 基础使用 / 可选属性 / 只读属性 / 任意属性(详细教程)

前言

在面向对象语言中,接口是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类去实现。

TypeScript 中的接口是一个非常灵活的概念,除了可用于 对类的一部分行为进行抽象 以外,也常用于对「对象的形状(Shape)」进行描述。

​TypeScript 的核心原则之一是对值所具有的结构进行类型检查,并且只要两个对象的结构一致,属性和方法的类型一致,则它们的类型就是一致的。​ 在TypeScript里,接口的作用就是为这些类型命名和为代码或第三方代码定义契约。

简单点说,在 TypeScript中,接口是一个很重要的特性,它让 TypeScript 具备了 JavaScript 所缺少的、描述较为复杂数据结构的能力。

引入主题

其实在 JavaScript 日常开发中,很多时候都需要接口来 “规范” 程序。

假设在 JavaScript 中定义一个函数,用来获取一个用户的姓名和年龄的字符串:

function getUserInfo(user) {return `name: ${user.name}, age: ${user.age}`
}

函数调用:

getUserInfo({name: "koala", age: 18})

您可能会问,我们写 JavaScript 的时候,这个再正常不过了吧?

但请注意,如果这个 getUserInfo() 在多人开发过程中,如果它是个公共函数(多个开发者都会调用),如果不是每个人点进来看函数对应注释,可能会出现以下错误的调用:

// 1: 直接调用,不知道还需要传参数
getUserInfo() // Uncaught TypeError: Cannot read property 'name' of undefined// 2: 只传递一个参数,不知道还有其他参数
console.log(getUserInfo({name: "王佳斌"})) // name: 王佳斌, age: undefined// 3: 参数知道传递多少个,但不知键名
getUserInfo({name: "王佳斌", width: 560}) // name: 王佳斌, age: undefined// ...

由于 JavaScript 是弱类型的语言,所以 并不会对我们传入的代码进行任何的检测

😦 有些错你自己都说不清楚,但是就出了问题。


那么如何解决呢?有请 Typescript 接口登场。

创建接口

指定的接口名称,最好与普通变量名 “有所区分” ,比如接口名首字母大写、首字母前缀(In_xxx)等。

在 Typescript 中,使用 interface 关键字来定义一个接口,其中 name 就是接口名称。

interface name {}

基础使用

Typescript 接口可以规定函数的 “形状”,也可以规定变量的 “形状”,下面有两个示例。

以下 JavaScript 例子(前面已经提到了,忘记的话往前翻):

function getUserInfo(user) {return `name: ${user.name}, age: ${user.age}`
}

这个所存在的问题大家已经知道了,下面用 Typescript 接口进行函数重构。

// 规定"形状"
interface Info {name: string;age: number;
}// 函数(冒号后跟上 "接口名")
function getUserInfo({ name, age }: Info) {return `name: ${name}, age: ${age}`
}// 正常都传递
console.log(getUserInfo({ name: '王佳斌', age: 123 }))
// 结果OK:"name: 王佳斌, age: 123" // 少传递一个
console.log(getUserInfo({ name: '王佳斌' }))
// Property 'age' is missing in type '{ name: string; }' but required in type 'Info'.
// 类型“{name:string;}”中缺少属性“age”,但类型“Info”中需要该属性。// 都不传递
console.log(getUserInfo())
// Expected 1 arguments, but got 0.
// 应为1个参数,但得到了0个。

你看,这些都是在编写代码时 TypeScript 提示的错误信息,这样就避免了在使用函数的时候传入不正确的参数。

注意:在定义接口时,不要把它理解为是在定义一个对象{} 括号包裹的是一个代码块,里面是声明语句,只不过声明的不是变量的值而是类型。声明也不用等号赋值,而是冒号指定类型。每条声明之前用换行分隔即可,也可以使用分号或者逗号。


另外,接口还可以被变量所使用(继承接口的 “形状”),如下代码所示:

// 规定"形状"
interface Info {name: string;age: number;
}// 变量 "继承" 接口
const student: Info = {name: '小王',age: 15
}// 测试变量
console.log(student)//{"name": "小王", "age": 15}// 错误用法(比如写一个 "Info" 接口不存在的参数)
const err: Info = {a: 1
}
// Object literal may only specify known properties, and 'a' does not exist in type 'Info'.
// 对象文字只能指定已知的财产,类型“Info”中不存在“a”。

可选属性

当然,TypeScript 中也允许不 “必传” 某些参数,有这个字段就做处理,没有就忽略。

如下代码所示,message 参数可以不传递。

// 使用 "?" 表示此参数非必传
interface Log {message?: string;
}// 函数
function print({ message }: Log) {console.log(message || '该参数没有传递~')
}// 传递参数
print({ message: 'hello' }) //"hello" // 不传递
print({}) //"该参数没有传递~" 

很好理解。

只读属性

TypeScript 支持将某些参数设置为 “只读”,用于限制只能在对象刚刚创建的时候修改其值,后续无法再修改。

如下代码所示,age 参数不可后期修改。

// 使用 "readonly" 关键字表示此参数"只读"
interface Info {name: string;readonly age: number;
}// 创建变量("age"只能初始的时候赋值一次)
const student: Info = {name: '小王',age: 15
}// 测试修改只读属性 "age"
student.age = 50
// Cannot assign to 'age' because it is a read-only property.
// 无法分配给“age”,因为它是只读属性。

此外 TypeScript 还提供了 ReadonlyArray<T> 类型,它与 Array<T> 相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改。

// 创建一个 "绝对不可修改" 的数组(number类型)
let arr: ReadonlyArray<number> = [1, 2, 3, 4]// 测试赋值
arr[0] = 10
// Index signature in type 'readonly number[]' only permits reading.
// 类型为“只读数字[]”的索引签名只允许读取。// 测试添加数组项
arr.push(5)
// Property 'push' does not exist on type 'readonly number[]'.
// 类型“只读数字[]”上不存在属性“push”。// 测试赋值数组长度
arr.length = 99
// Cannot assign to 'length' because it is a read-only property.
// 无法分配给“length”,因为它是只读属性。

任意属性

有时候我们希望一个接口中除了包含必选和可选属性之外,还允许有其他的任意属性,这时我们可以使用 索引签名 的形式来满足上述要求。

如下代码所示,除了 name 必传外,后面你可以随意传递参数。

// 使用 "[propName: string]: any" 支持任意类型
interface Person {name: string;[propName: string]: any;
}// 只传递必填,其他参数不要
const a: Person = { name: '小王' }
console.log(a) //{"name": "小王"}// 传递必填,其他参数随意传递
const b: Person = { name: '小王', age: 15, sex: '男' }
console.log(b) //{"name": "小王", "age": 15, "sex": "男"}

很好理解。