1. 解释
接口是对 JavaScript 本身的随意性进行约束,通过定义一个接口,约定了变量、类、函数等应该按照什么样的格式进行声明,实现多人合作的一致性。TypeScript 编译器依赖接口用于类型检查,最终编译为 JavaScript 后,接口将会被移除。
- 1
- 2
- 3
- 4
2. 应用场景
在声明一个对象、函数或者类时,先定义接口,确保其数据结构的一致性。
在多人协作时,定义接口尤为重要。
3. 接口的好处
过去我们写 JavaScript 定义一个函数:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
之前我们写 JavaScript 这样是很正常的,但同时你可能会遇到下面这些问题:
- 1
- 2
相信原因你也知道,JavaScript 是 弱类型
语言,并不会对传入的参数进行任何检测,错误在运行时才被发现。那么通过定义 接口
,在编译阶段甚至开发阶段就避免掉这类错误,接口将检查类型是否和某种结构做匹配。
3.1 举例说明
下面通过接口的方式重写之前的例子:
代码解释: 代码中,定义了一个接口 Clothes
,在传入的变量 clothes
中,它的类型为 Clothes
。这样,就约束了这个传入对象的 外形
与接口定义一致。只要传入的对象满足上面的类型约束,那么它就是被允许的。
Tips:
定义接口要
首字母大写
。只需要关注值的
外形
,并不像其他语言一样,定义接口是为了实现。如果没有特殊声明,定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的,赋值的时候,变量的形状必须和接口的形状保持一致。
4. 接口的属性
4.1 可选属性
接口中的属性不全是必需的。可选属性的含义是该属性在被变量定义时可以不存在。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ?
符号。
这时,仍不允许添加未定义的属性,如果引用了不存在的属性时 TS 将直接捕获错误。
4.2 只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。你可以在属性名前用 readonly
来指定只读属性,比如价格是不能被修改的:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
TypeScript 可以通过 ReadonlyArray<T>
设置数组为只读,那么它的所有写方法都会失效。
- 1
- 2
代码解释: 代码中的泛型语法在之后会有专门的小节介绍。
4.2.1 readonly
vs const
最简单判断该用 readonly
还是 const
的方法是看要把它做为变量使用还是做为一个属性。做为 变量
使用的话用 const,若做为 属性
则使用 readonly。
4.3 任意属性
有时候我们希望接口允许有任意的属性,语法是用 []
将属性包裹起来:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
代码解释: 这里的接口 Clothes
可以有任意数量的属性,并且只要它们不是 color
size
和 price
,那么就无所谓它们的类型是什么。
- 项目案例:使用 axios 库发起 HTTP 传输的时候,可以写入一个自定义的属性,就是因为源码中定义了一个任意属性:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
5. 函数类型
除了描述带有属性的普通对象外,接口也可以描述函数类型。
为了使接口表示函数类型,我们需要给接口定义一个调用签名。 它就像是一个只有 参数列表
和 返回值类型
的函数定义。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。你可以改变函数的参数名,只要保证函数参数的位置不变。函数的参数会被逐个进行检查:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
如果你不想指定类型,TypeScript 的类型系统会推断出参数类型,因为函数直接赋值给了 SearchFunc 类型变量。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
如果接口中的函数类型带有函数名,下面两种书写方式是等价的:
- 1
- 2
- 3
- 4
6. 可索引类型
可索引类型接口读起来有些拗口,直接看例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
再来看定义可索引类型接口:
- 1
- 2
- 3
- 4
- 5
- 6
示例中索引签名是 number类型
,返回值是字符串类型。
另外还有一种索引签名是 字符串类型
。我们可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。通过下面的例子理解这句话:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
代码解释:
第 12 行,语法错误是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。
7. 类类型
我们希望类的实现必须遵循接口定义,那么可以使用 implements
关键字来确保兼容性。
这种类型的接口在传统面向对象语言中最为常见,比如 Java 中接口就是这种类类型的接口。这种接口与抽象类比较相似,但是接口只能含有抽象方法和成员属性,实现类中必须实现接口中所有的抽象方法和成员属性。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
你也可以在接口中描述一个方法,在类里实现它:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
接口描述了类的公共部分,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。
8. 继承接口
和类一样,接口也可以通过关键字 extents
相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
一个接口可以继承多个接口,创建出多个接口的合成接口。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
9. 混合类型
在前面已经介绍,接口可以描述函数、对象的方法或者对象的属性。
有时希望一个对象同时具有上面提到多种类型,比如一个对象可以当做函数使用,同时又具有属性和方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
代码解释:
第 1 行,声明一个接口,如果只有 (start: number): string
一个成员,那么这个接口就是函数接口,同时还具有其他两个成员,可以用来描述对象的属性和方法,这样就构成了一个混合接口。
第 7 行,创建一个 getCounter()
函数,它的返回值是 Counter 类型的。
- 1
第 8 行,通过类型断言,将函数对象转换为 Counter
类型,转换后的对象不但实现了函数接口的描述,使之成为一个函数,还具有 interval 属性和 reset() 方法。断言成功的条件是,两个数据类型只要有一方可以赋值给另一方,这里函数类型数据不能赋值给接口类型的变量,因为它不具有 interval 属性和 reset() 方法。
类型断言在之后的小节也会单节介绍。
10. 小结
本节介绍了接口的基本用法及其使用场景,接口在 TypeScript 中至关重要,TypeScript 编译器依赖接口用于类型检查。