TypeScript 接口(Interface)
在下一节学习完类之后,你会知道类也可以作为接口来使用。接口的种类繁多,在学习过程中一定要亲手编写,以达到灵活使用。
1. 解释
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。——官方定义
接口是对 JavaScript 本身的随意性进行约束,通过定义一个接口,约定了变量、类、函数等应该按照什么样的格式进行声明,实现多人合作的一致性。TypeScript 编译器依赖接口用于类型检查,最终编译为 JavaScript 后,接口将会被移除。
// 语法格式
interface DemoInterface {
}
2. 应用场景
在多人协作时,定义接口尤为重要。
3. 接口的好处
function getClothesInfo(clothes) {
console.log(clothes.price)
}
let myClothes = {
color: 'black',
size: 'XL',
price:
}
getClothesInfo(myClothes)
之前我们写 JavaScript 这样是很正常的,但同时你可能会遇到下面这些问题:
getClothesInfo() // Uncaught TypeError: Cannot read property 'price' of undefined
getClothesInfo({ color: 'black' }) // undefined
相信原因你也知道,JavaScript 是 弱类型
语言,并不会对传入的参数进行任何检测,错误在运行时才被发现。那么通过定义 接口
,在编译阶段甚至开发阶段就避免掉这类错误,接口将检查类型是否和某种结构做匹配。
3.1 举例说明
下面通过接口的方式重写之前的例子:
interface Clothes {
color: string;
size: string;
price: number;
}
function getClothesInfo(clothes: Clothes) {
console.log(clothes.price)
}
let myClothes: Clothes = {
color: 'black',
size: 'XL',
price:
}
getClothesInfo(myClothes)
代码解释: 代码中,定义了一个接口 Clothes
,在传入的变量 clothes
中,它的类型为 Clothes
。这样,就约束了这个传入对象的 外形
与接口定义一致。只要传入的对象满足上面的类型约束,那么它就是被允许的。
Tips:
4. 接口的属性
4.1 可选属性
// 语法
interface Clothes {
color?: string;
size: string;
price: number;
}
// 这里可以不定义属性 color
let myClothes: Clothes = {
size: 'XL',
price:
}
4.2 只读属性
// 语法
interface Clothes {
color?: string;
size: string;
readonly price: number;
}
// 创建的时候给 price 赋值
let myClothes: Clothes = {
size: 'XL',
price:
}
// 不可修改
myClothes.price =
// error TS2540: Cannot assign to 'price' because it is a constant or a read-only property
TypeScript 可以通过 ReadonlyArray<T>
设置数组为只读,那么它的所有写方法都会失效。
let arr: ReadonlyArray<number> = [,,,,];
arr[] = ; // Index signature in type 'readonly number[]' only permits reading
4.2.1 readonly
vs const
4.3 任意属性
// 语法
interface Clothes {
color?: string;
size: string;
readonly price: number;
[propName: string]: any;
}
// 任意属性 activity
let myClothes: Clothes = {
size: 'XL',
price: ,
activity: 'coupon'
}
this.$axios({
method: 'put',
url: '/cms/user',
data: {
nickname: this.nickname,
},
showBackend: true,
})
5. 函数类型
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean {
return source.search(subString) > -;
}
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
// source => src, subString => sub
mySearch = function(src: string, sub: string): boolean {
return src.search(sub) > -;
}
如果你不想指定类型,TypeScript 的类型系统会推断出参数类型,因为函数直接赋值给了 SearchFunc 类型变量。
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(src, sub) {
let result = src.search(sub);
return result > -;
}
interface Calculate {
add(x: number, y: number): number
multiply: (x: number, y: number) => number
}
6. 可索引类型
可索引类型接口读起来有些拗口,直接看例子:
// 正常的js代码
let arr = [, , , , ]
let obj = {
brand: 'imooc',
type: 'education'
}
arr[]
obj['brand']
再来看定义可索引类型接口:
interface ScenicInterface {
[index: number]: string
}
let arr: ScenicInterface = ['西湖', '华山', '故宫']
let favorite: string = arr[]
示例中索引签名是 number类型
,返回值是字符串类型。
另外还有一种索引签名是 字符串类型
。我们可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。通过下面的例子理解这句话:
// 正确
interface Foo {
[index: string]: number;
x: number;
y: number;
}
// 错误
interface Bar {
[index: string]: number;
x: number;
y: string; // Error: y 属性必须为 number 类型
}
代码解释:
第 12 行,语法错误是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。
7. 类类型
我们希望类的实现必须遵循接口定义,那么可以使用 implements
关键字来确保兼容性。
interface AnimalInterface {
name: string;
}
class Dog implements AnimalInterface {
name: string;
constructor(name: string){
this.name = name
}
}
interface AnimalInterface {
name: string
eat(m: number): string
}
class Dog implements AnimalInterface {
name: string;
constructor(name: string){
this.name = name
}
eat(m: number) {
return `${this.name}吃肉${m}分钟`
}
}
接口描述了类的公共部分,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。
8. 继承接口
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = {} as Square;
// 继承了 Shape 的属性
square.color = "blue";
square.sideLength = ;
一个接口可以继承多个接口,创建出多个接口的合成接口。
interface Shape {
color: string;
}
interface Penstroke {
penWidth: number;
}
interface Square extends Shape, Penstroke {
sideLength: number;
}
let square = {} as Square;
square.color = "blue";
square.sideLength = ;
square.penWidth = ;
9. 混合类型
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = function (start: number) { } as Counter;
counter.interval = ;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c();
c.reset();
c.interval = ;
代码解释:
第 1 行,声明一个接口,如果只有 (start: number): string
一个成员,那么这个接口就是函数接口,同时还具有其他两个成员,可以用来描述对象的属性和方法,这样就构成了一个混合接口。
let counter = function (start: number) { } as Counter;
第 8 行,通过类型断言,将函数对象转换为 Counter
类型,转换后的对象不但实现了函数接口的描述,使之成为一个函数,还具有 interval 属性和 reset() 方法。断言成功的条件是,两个数据类型只要有一方可以赋值给另一方,这里函数类型数据不能赋值给接口类型的变量,因为它不具有 interval 属性和 reset() 方法。
类型断言在之后的小节也会单节介绍。
10. 小结
本节介绍了接口的基本用法及其使用场景,接口在 TypeScript 中至关重要,TypeScript 编译器依赖接口用于类型检查。