接口
基本概念
在 TypeScript 当中,接口用于简单地描述类型,一个 employee 接口的定义如下:
interface Employee {
name: string // 行间加分号、逗号、不加都可以
salary: number
}
使用 interface:
const emp1: Employee = {
name: 'John',
salary: 8000,
}
可以看到,interface 为类型的实例化提供了约束,Employee 类型只能包含 name 和 salary 两个变量。
一个更灵活的接口定义方法如下,在这个接口当中,我们进一步引入 bonus,但是 bonus 未必一定有:
interface Employee {
name: string;
salary: number
bonus?: number // 使用 ? 来表明 bonus 成员不一定在 Employee 接口当中有定义
}
function updateBonus(e: Employee, p: number) {
if(!e.bonus) {
e.bonus = e.salary * p
}
}
我们通常不在接口当中定义方法。
可以在 interface 当中设置只读字段,在字段名之前加入 readonly 关键字即可,但是这个行为不是很常用。
接口的高级技巧
可选参数的串联 && 非空断言
假定现在我们定义的 Employee 接口当中,name 由两个字段组成,且包含可选字段,如果我们为接口定义了一个函数,用于判断接口当中的 name 字段是否合法,由于 name 当中包含可选字段,我们需要使用多个 if - else 来判断字段是否存在,产生大量的冗余代码。此时我们可以使用“可选参数的串联”这个技巧来规避上述冗余。一个糟糕的版本如下:
interface Employee {
name?: {
first: string
last: string
}
salary: number
bonus?: number
}
function hasBadName(e: Employee) {
if(e.name) return e.name.first.startsWith('AAA')
else return true
}
使用可选参数串联进行改进的版本如下:
interface Employee {
name?: {
first?: string
last: string
}
salary: number
bonus?: number
}
function hasBadName(e: Employee) {
return e.name?.first?.startsWith("AAA")
}
可选字段串联同样是 typescript 的语法糖。可以使用!
替代?
,此时将告诉编译器,如果出错,后果自负,必须要在编译阶段报错。使用!
表示字段一定不空,不需要编译器报错,这条性质叫做非空断言。
接口的扩展
假定现在我们已经有了接口 HasName:
interface HasName {
name?: {
first?: string
last: string
}
}
我们想在 Employee 当中直接使用 HasName 当中的字段作为 Employee 的字段,可以使用 extends 关键字完成接口的扩展:
interface Employee extends HasName{
salary: number
bonus?: number
}
interface HasName {
name?: {
first?: string
last: string
}
}
类型的并 && 类型断言
假定现在我们有两个不同的接口,并且有一个函数,需要使用这个函数来判断传入的参数是哪个接口,根据传入的接口来执行特定的任务。如果两个接口具有相同的字段,那么对于函数来说,只能使用字段的并集,这条性质叫做接口的并:
interface WxButton {
visible: boolean
enabled: boolean
onClick(): void
}
interface WxImage {
visible: boolean
src: string
width: number
height: number
}
// Union of types
function hideElement(e: WxButton | WxImage) {
// 只能使用共有的属性, 比如 e.visible
// ... ... ...
}
如果想要根据传入的实参判断接口的类型,我们不能够使用 typeof 关键字,而应该使用接口的断言:
function processElement(e: WxButton | WxImage) {
// 判断传入的 e 的类型
if((e as any).onClick) {
// 使用 as any 告诉 TS 编译器不要帮助我们判断
// e as WxButton 叫做类型断言
const btn = e as WxButton
btn.onClick()
} else {
const img = e as WxImage
// e as WxImage 是类型断言
console.log(img.src)
}
}
一个更优雅的写法如下,在这个写法中,新定义了一个辅助函数,它的返回值是e is WxButton
:
function elegentProcessElement(e: WxButton | WxImage) {
if(isButton(e)) {
e.onClick()
} else {
console.log(e.src)
}
}
function isButton(e: WxButton | WxImage): e is WxButton {
return (e as WxButton).onClick !== undefined
}