接口就像协议规范一样。比如USB协议,而各个厂商可以有不同的实现方式,但是要遵循这个协议,实现出来的USB设备才能在其它遵循USB协议的设备上使用。在这里不同的厂商就是接口的不同的实现者。
接口定义与可选属性
interface People {
nick: string;
age: number;
phone?: string;//可选属性
}
function showPeople(people: People): void {
console.log(`${people.nick} age is ${people.age}`);
if (people.phone) {
console.log(` and the phone number is ${people.phone}`);
}
}
let people = {nick: 'Blob', age: 22, phone: '12345678910'};
let people2 = {nick: 'Anni', age: 20};
showPeople(people);
showPeople(people2);
/* output
Blob age is 22
and the phone number is 12345678910
Anni age is 20
*/
- interface关键字用来定义一个接口。
- 属性名称里面带?的表示这个属性是一个可选属性。
- showPeople方法接收一个***People ***接口形式的变量。
- 我们定义了两个变量people和people2,就像是厂商一样,这两个是接口的实现者,但是TypeScript里面不这么叫,我们说它们的属性与接口是兼容的。这也是为什么调用showPeople方法的时候可以传入这两个变量。接口有一个可选属性,所以people2里面没有这个可选属性phone,但是也能作为方法的参数传递进去。至于这两个变量的属性定义顺序跟接口属性定义顺序没有关系,你可以随意调整,TypeScript的类型检查器只会检查是否有这个属性和属性的类型是否与接口定义的一致。
只读属性
- 只读属性
// 只读属性
interface Blob {
firstName: string;
readonly weight: number;
readonly height: number;
}
function showBlob(blob: Blob) {
console.log(`first name is ${blob.firstName},
weight is ${blob.weight},
height is ${blob.height}`);
//blob.weight -= 10; // 错误,只读属性不能修改
}
关键字readonly放到属性名称前,表示这个属性在初始化以后是不可以更改的。
- 只读数组
// 只读数组
let rwArray: Array<number> = [1, 2, 3];
let rArray: ReadonlyArray<number> = rwArray;
//rArray[0] = 1; //错误,只读数组不能修改内容
//rwArray = rArray; //错误,只读数组不能赋值给普通数组
rwArray = rArray as Array<number>;//类型断言
//rwArray = rArray as string[];//错误,类型不匹配
- 定义一个数组可以使用类型[]或者Array[类型],同样定义一个只读数组可以使用关键字ReadonlyArray<类型>。
- 一个数组可以赋值给只读数组,但是反过来不行。一定要把只读数组赋值给数组的话,可以使用类型断言。
- readonly和const
用来定义一个接口或类的属性是只读的时候应该用readonly,而定义一个不可变的变量的时候用const。
额外的属性检查
interface Music {
musicName?: string;
time?: number;
picture?: string;
}
function playMusic(music: Music): void {
console.log(`${music.musicName},
time remain ${music.time},
picture is ${music.picture}`);
}
//playMusic({aa: 'haha'});
//类型“{ aa: string; }”与类型“Music”不具有相同的属性。
let music = {aa: 'hello', time: 13};
playMusic(music);
- 虽然接口的属性都是可选属性,但是playMusic({aa: 'haha'})却是错误的。
- {aa: 'haha'}属于一个字面量定义的对象,即没有对象名称的一个匿名对象,对于这类对象,当将这类对象赋值给变量或作为参数传递的时候,TypeScript会对它们进行额外属性检查,即将它们与目标期望的类型对比,如果它们之中包含了期望类型不包含的属性,就会报错(在这个例子中就是拿 对象{aa: 'haha'}与 方法playMusic 期望的参数类型 接口Music 对比,发现接口中并没有aa这个属性,就会报错)。
- 解决办法一
interface Music {
musicName?: string;
time?: number;
picture?: string;
[props: string]: any;
}
function playMusic(music: Music): void {
console.log(`${music.musicName},
time remain ${music.time},
picture is ${music.picture}`);
}
playMusic({aa: 'haha'});
[props: string]: any 表示我们的接口可以有任意数量的属性,只要属性名不会与已经明确定义的属性名冲突就行,至于是什么类型我们不关心。
- 解决办法二
interface Music {
musicName?: string;
time?: number;
picture?: string;
}
function playMusic(music: Music): void {
console.log(`${music.musicName},
time remain ${music.time},
picture is ${music.picture}`);
}
playMusic({aa: 'haha'} as Music);
通过类型断言来解决这个问题是最简单的方式,相当于我们做了个强制类型转换,告诉编译器,它就是期待的类型。
函数类型
为了使用接口表示函数类型,我们需要给接口定义一个函数签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
interface Phone {
(color: string, width: number, height: number): void
}
let phone = function(c: string, w: number, h: number): void {
//函数的参数名不需要与接口里定义的名字相匹配
console.log(`color is ${c}, width is ${w}, height is ${h}`);
}
phone('Red', 15.6, 6.8);//调用
// output
//color is Red, width is 15.6, height is 6.8
可索引的类型
interface Names {
[index: number]: string;
}
let names: Names = ['Blob', 'John', 'Anni'];
let first = names[0]
console.log(first);//'Blob'
console.log(names[3]);//undefined
可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。这里定义的索引类型是number,索引返回类型是string。
索引类型还可以是string类型。
interface Index {
[index: number]: string;
[index: string]: string;
name: string;
color: string;
//age: number;//错误,与索引返回类型不匹配
}
let indexArg = {0: 'aa', 1: '11', 2: '12', name: 'Index', color: 'Green'};
console.log(indexArg[0]);//aa
console.log(indexArg['0']);//aa
console.log(indexArg['name']);//索引的方式访问,输出 Index
console.log(indexArg.name);//属性的方式访问,输出 Index
- 接口中定义了两种索引类型,一种number,一种是string。indexArg[0] 与 indexArg['0']都返回同一个属性。
- 同时使用两种类型的索引的时候,数字索引的返回类型必须是字符串索引返回类型的子类型。 这是因为当使用 number来索引时,TypeScript会将它转换成string然后再去索引对象。 也就是indexArg[0] 会被转换成 indexArg['0']。
- 其它的属性的类型也要与索引返回的类型相同。
不积跬步,无以至千里;不积小流,无以成江海。
网友评论