接口interface
对象类型接口
定义接口
interface List {
id: number;
name: string;
}
interface Result {
data: List[]
}
function render(result: Result) {
result.data.forEach((value) => {
console.log(value.id, value.name)
})
}
let result = {
data: [
{id: 1, name: 'A'},
{id: 2, name: 'B'}
]
}
render(result) // 1 "A"
// 2 "B"
PS:
后端有时候会传来约定之外的字段,ts并不报错。所以只要传入的对象是必要条件就是被允许的:
let result = {
data: [
{id: 1, name: 'A', sex: 'male'},
{id: 2, name: 'B'}
]
}
但是我们直接传入对象字面量,ts就会对额外的字段进行类型检查:
render({
data: [
{id: 1, name: 'A', sex: 'male'}, // 提示错误
{id: 2, name: 'B'}
]
})
绕过类型检查的方法
第一种方式: 将对象赋值给一个变量
第二种方式是类型断言:as + 对象的类型,明确告诉编译器,对象的类型就是Result,编译器就会绕过类型检查
render({
data: [
{id: 1, name: 'A', sex: 'male'}, // 提示错误
{id: 2, name: 'B'}
]
} as Result)
PS:
类型断言的另一种不建议用的方法,就是在对象前面加上<Result>,但是在React种容易产生歧义。
第三种方法是使用字符串索引签名,格式如下:
interface List{
id:number;
name:string;
[x,string]:any;
}
PS:
该签名的含义是用任意的字符串去索引List,会得到任意的结果,这样List就支持多个属性了。
可选属性(属性+格式)
假设有个新需求,需要判断value中是否有个新字段,如果有,就把它打印出来:
interface List {
id: number;
name: string;
age?: number;
}
interface Result {
data: List[]
}
function render(result: Result) {
result.data.forEach((value) => {
console.log(value.id, value.name)
if(value.age) {
console.log(value.age)
}
})
}
let result = {
data: [
{id: 1, name: 'A', sex: 'male'},
{id: 2, name: 'B'}
]
}
render(result)
PS:
在render函数中进行判断,会提示错误,这时我们在List中添加属性age,result会报错,这就需要我们使用可选属性了。
只读属性(readonly + 属性格式)
只读属性不允许修改
interface List {
readonly id: number;
}
function render(result: Result) {
result.data.forEach((value) => {
value.id ++ // 提示错误
})
}
可索引类型的接口
以上属性的个数是固定的,当我们不确定属性个数时,就要用到可索引类型的接口,常用的有两种:
用数字索引的接口
interface StringArray {
[index: number]: string
}
PS:
含义是用任意的数字去索引StringArray,会得到一个string,这就相当于声明了一个字符串类型的数组。
比如:
let chars: StringArray = ['A', 'B']
用字符串索引的接口
interface Names {
[x: string]: string
}
PS:
含义是用任意的字符串索引Names,得到的结果都是string,这样我们就不能并列声明number类型的成员了:
interface Names {
[x: string]: string
y: number // 提示错误
z: string
}
这两种索引签名是可以混用的
interface Names {
[x: string]: string
[z: number]: string
}
PS:
需要注意的是,数字索引签名的返回值一定要是字符串索引签名返回值的子类型,这是因为js会进行类型转换,将number转成string,这样就能保证类型的兼容性。
比如下面这样就会报错:
interface Names {
[x: string]: string
[z: number]: number // 提示错误
}
但这样就可以:
interface Names {
[x: string]: any
[z: number]: number
}
函数类型接口
定义接口
用变量定义函数类型
let add: (x: number, y: number) => number
用接口定义函数类型
interface Add {
(x: number, y: number): number
}
用类型别名定义函数类型
type Add = (x: number, y: number) => number
let add: Add = (a, b) => a + b
混合类型的接口
这种接口既可以定义一个函数,也可以像对象一样拥有属性和方法
interface Lib {
(): void
version: string
doSomething(): void
}
let lib: Lib = (() => {}) as Lib
lib.version = '1.0'
lib.doSomething = () => {}
我们可以创造多个lib实例
function getLib() {
let lib: Lib = (() => {}) as Lib
lib.version = '1.0'
lib.doSomething = () => {}
return lib
}
let lib1 = getLib()
lib1()
lib1.doSomething()
let lib2 = getLib()