一、前言
TS新增了一个重要概念:接口,分为对象类型接口和函数类型接口
接口可以约束对象,函数,类的结构和类型,是一种代码协作必须遵守的契约
接口的定义方式:
使用interface关键字
二、对象类型接口
接口中可定义 确定属性、可选属性、任意属性、只读属性
1、确定属性
interface UserInfo {
name: string;
age: number;
}
const myInfo: UserInfo = {
name: "haha",
age: 20
};
接口中约束好的确定属性,定义对象变量的时候 不能少也 不能多
2、可选属性
interface UserInfo {
name: string;
age: number;
sex?:string
}
const myInfo: UserInfo = {
name: "haha",
age: 20
};
接口中的可选属性,是表示在对象变量中可以不存在
3、任意属性
interface UserInfo {
name: string;
age: number;
sex?:string ;
[propName: string]:any;
}
const myInfo: UserInfo = {
name: "haha",
age: 20,
test1: 'lala',
test2: 'ff',
test3:123
};
注:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是任意属性类型的子类;
定义了任意属性后,对象变量中的属性个数才可以出现比接口的属性数量多的情况
4、只读属性
interface UserInfo {
readonly id: number;
name: string;
age: number;
sex?: string;
[propName: string]: any;
}
const myInfo: UserInfo = {
id: 1,
name: "haha",
age: 20,
test1: "lala",
test2: "ff",
test3: 123
};
只读属性也是确定属性,在对象变量定义的时候必须有值,此后不能修改
对象接口示例
以查询商品列表接口API为例:
// 接口声明:API协议约定返回格式
interface ResponseData {
resCode: number;
resData: ResultData[];
message: string;
}
// 数据接口声明
interface ResultData {
productId: number;
productName: string;
}
符合接口约定的对象:
let resultData = {
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战" },
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
}
输出函数对结果进行打印:
function render(res: ResponseData) {
console.log(res.resCode, res.message)
res.resData.forEach((obj) => {
console.log(obj.productId, obj.productName)
})
}
render(resultData);
输出:
0 "success"
1 "TypeScipt实战"
2 "TypeScipt从入门到精通"
额外属性
在接口的实际调用中,后端也经常会传递约定之外的字段,比如:
let resultData = {
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark:""}, // 接口约定以外的remark字段
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
}
此时,并没有报错,TS允许这种情况的发生
只要传入的对象满足接口的必要条件就可以被允许,即使传入多余的字段也可以通过类型检查
但也有例外,如果直接传入对象字面量,TS就会对额外的字段进行类型检查
以下方式会报错:
render({
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark: "备注"},
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
})
绕过检查的方法有3种:
1,将对象字面量赋值给一个变量
将对象字面量赋值给一个变量后,在render不再报错:
let result = {
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark: "备注"},
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
}
render(result)
2,使用类型断言
使用类型断言方式,明确告诉编译器类型是什么,编译器就会绕过类型检查
方法1:
object as targetInterface
render({
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark:""},
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
} as ResponseData)
方法二:
<targetInterface>object
render(<ResponseData>{
resCode: 0,
resData: [
{ productId: 1, productName:"TypeScipt实战", remark: "备注"},
{ productId: 2, productName:"TypeScipt从入门到精通" },
],
message: "success"
})
3,使用字符串索引签名
添加字符串索引签名:
interface ResultData {
productId: number;
productName: string;
[remark: string]: any; // 字符串索引签名
}
表示用任意字符串去索引List,可得到任意结果,此时List可以实现支持多个属性
三、函数接口
函数定义方式
1,在TS中,可以使用一个变量直接定义函数:
// 1,使用变量定义函数
let add: (x: number, y: number) => number
= (x, y){
return x+y;
};
add(1,2)
2,还可以使用接口来定义函数:
// 2,使用接口定义函数
interface Add {
(x: number, y: number): number
}
let myFunc: Add = function(x, y){
return x+y;
};
myFunc(1,2);
3,使用类型别名来定义函数:
类型别名使用type关键字,相当于为函数类型起一个名字
// 3,使用类型别名来定义函数
type Add = (x: number, y: number) => number
四、可索引类型的接口
当不能确定接口中有多少个属性时,可以使用可索引类型接口
可索引类型接口可以用数字去索引,也可以用字符串去索引
1,数字索引接口:
声明一个数字索引类型的接口:
表示用任意数字去索引numberIndex都会得到一个string
interface numberIndex {
[x: number]: string
}
// 相当于声明了一个字符串类型的数组
let chars: numberIndex = ['A', 'B']
2,字符串索引接口:
声明一个字符串索引类型的接口:
表示用任意的字符串去索引stringIndex得到的结果都是string
interface stringIndex {
[x: string]: string
}
这样声明后,就不能声明number类型的成员了,会报错
interface stringIndex {
[x: string]: string
y: number;// Property 'y' of type 'number' is not assignable to string index type 'string'.
}
3,两种索引签名混用:
在上边的字符串索引接口stringIndex中,添加数字索引签名
interface stringIndex {
[x: string]: string
[z: number]: string
}
这样做.接口既可以用数字索引Names也可以用字符串索引Names
但需要注意:数字索引的返回值,一定要是字符串返回类型的子类型
这是因为JS会进行类型转换,将number转换成string,这样就能保证类型的兼容性
比如:将数组索引的返回值改成number:
interface stringIndex {
[x: string]: string
[z: number]: number // // Numeric index type 'number' is not assignable to string index type 'string'.
}
这样就和String类型不兼容了,要取一个能够兼容number的类型,比如any:
interface stringIndex {
[x: string]: any
[z: number]: number // Numeric index type 'number' is not assignable to string index type 'string'.
}