接口
TS中的接口(Interface)是用于约束类、对象、函数的规范
接口定义语句不会出现在编译结果中
约束对象
使用Interface关键字定义接口
ts
// 定义用户接口,要求每一个用户对象中必须包含一个name字符串属性和一个age数字属性
interface User {
name: string
age: number
}
类似于对象类型约束
tstype User = { name: string, age: number }
约束函数
函数可以是单独的函数,也可以作为对象的属性,即方法
约束单独的函数:
ts
interface Condition { // 这里的大括号是划分接口定义代码的符号,不是要求Condition为对象
(n: number): boolean
}
function sum(nums:number[], callback: Condition){
let result = 0;
nums.forEach((num)=>{
if(callback(num)){
result += num;
}
});
}
类似于
ts// 定义sum函数,函数接收一个数字数组和一个回调函数,数组元素只有满足回调函数中的条件时,才会参与求和 type Condition = (n:number)=>boolean; // 下面同上 // type Condition = { // (n: number): boolean // } function sum(nums:number[], callback: Condition){}
约束方法:
ts
interface User {
// sayHello是一个函数(方法),方法不接收任何参数,不返回任何值
sayHello: ()=>void
// 等同下面的写法
sayHello():void
}
继承
接口继承了其他接口,子接口中会自动包含父接口的字段类型限制
ts
interface A {
a: number
}
interface B extends A {
b: string
}
let b: B = {
a: 1,
b: "2"
}
接口允许多继承
ts
interface A {
a: number
}
interface B {
b: string
}
interface C extends A, B {
c: boolean
}
let c: C = {
a: 1,
b: "2",
c: true
}
子接口中不允许出现和父接口同名且类型不同的字段
ts
interface A {
a: number
}
interface B {
b: string
}
interface C extends A, B {
a: string // 与父接口A中的a字段类型冲突,不允许
b: string // 与付接口B中的b字段类型不冲突,允许
c: boolean
}
let c: C = {
a: 1,
b: "2",
c: true
}
可选属性
约束属性时加上?,表示该属性可以为undefined
ts
interface User {
name: string // 必须存在
height?: number // 等同于 height: number|undefined
}
readonly
被readonly修饰符修饰的目标是只读的
支持修饰对象成员以及数组、元组类型
修饰成员,成员初始化后就不允许被修改
ts
interface User {
readonly id: string
}
const user = {
id: "1"
};
user.id = "2"; // ❌️
修饰数组&元组字面量类型,引用类型中的内容就不允许修改
ts
const arr: readonly number[] = [1, 2, 3];
// 等同于
const arr: ReadonlyArray<number> = [1, 2, 3];
arr.push(); // ❌️
arr[0] = -1; // ❌️
只读修饰符只参与编译,不会出现在编译结果中
类型兼容性
如果一个变量(或字面量)能够赋值给另一个变量,则说明这两者的类型是兼容的
不同类型的兼容性策略有所不同:
-
基本类型:完全匹配,例如不能将boolean类型的值赋值给被string类型约束的变量
-
对象类型:引用类型的类型兼容型根据赋的值的不同有不同的处理
将一个变量赋值给另一个变量时,会采用子结构辩型法进行类型判断,目标类型包含一些特征,如果被赋予的值满足了所有特征,那么两者就是兼容的
tsinterface Duck { sound: "嘎嘎嘎", // 特征1: 拥有sound属性,为"嘎嘎嘎"字面量类型 swim(): void // 特征2: 拥有swim方法,方法不接收参数也不返回内容 } const person = { name: "伪装成鸭子的人", age: 20, sound: "嘎嘎嘎" as "嘎嘎嘎", // 类型断言 swim(){ console.log(this.name + "正在游泳,并发出了【" + this.sound + "】的声音"); } } const duck:Duck = person; // 因为person满足Duck类型的所有特征,因此允许将person赋值给duck直接将字面量赋值给变量时,会进行更加严格的检查
tsinterface Duck { sound: "嘎嘎嘎", swim(): void } const duck:Duck = { // 虽然对象字面量满足Duck类型的所有特征,但由于是字面量赋值,所以不允许字面量中出现sound和swim以外的其他属性 name: "伪装成鸭子的人", age: 20, sound: "嘎嘎嘎" as "嘎嘎嘎", swim(){ console.log(this.name + "正在游泳,并发出了【" + this.sound + "】的声音"); } }使用字面量赋值一定是有意为之的,因为编码是一定知道变量的类型和值的类型具体类型,此时就应该更严格地进行约束
而第三方变量可能是接口获取或其他方式获取得到的,第三方变量可能并不能确定被赋值的变量的类型,因此降低了约束范围
-
函数类型:函数类型的约束主要体现在两个方面,参数和返回值
参数:被赋值的函数字面量的形参数量可以少于(但不能多)函数类型规定的形参数量(这也符合的开发习惯,因为并不是所有的参数都一定需要使用到)
tsinterface Condition { (num: number, index: number): boolean } function sum(nums, callback: Condition){ let result = 0; nums.forEach((num, index)=>{ if(callback(num, index)){ result += num; } }); return result; } sum([1, 2, 3, 4, 5], (num)=>{ // 正确 return num > 3; }); sum([1, 2, 3, 4, 5], (num, index)=>{ // 正确 return index > 3; });返回值:如果不要求返回内容,则函数定义时可返回也可不返回;如果要求返回内容,则实际的返回值必须满足返回类型的要求
类型断言
如果确定某个变量的值属于某个类型,但是TS对该变量的类型的推断又有问题,那么就可以使用类型断言
ts
type Test = "test";
const str = "test";
// 正常情况下,TS会识别str为字符串类型,但实际上它是"test"字面量类型,此时就可以使用类型断言
const str = "test" as Test;
// 或者
const str = <Test>"test";