接口和类型兼容性
接口: interface
扩展类型 - 接口
扩展类型: 类型别名、枚举、接口、类
TypeScript的接口:用于约束类、对象、函数的契约(标准)
类似于电脑的接口或后端接口,其实就是一种标准
契约(标准)的形式:
- API文档(弱标准)
- 代码约束(强标准)
和类型别名一样,不会出现在编译结果中,和类型别名区别主要在类这一块
- 接口约束对象
interface User {
name: string
age: number
sayHello: () => void
//sayHello(): void
}
let u: User = {
name: "saiwen",
age: 22,
sayHello() {
console.log("sayHello");
}
};
这不就是跟类型别名一样吗?个人建议使用接口约束对象。
- 接口约束函数
一个简单例子,数组中奇数相加之和
interface Condition {
(n: number): boolean
}
function sum(numbers: number[], callback: Condition): number {
let sum = 0;
numbers.forEach((n) => {
if (callback(n)) {
sum += n;
}
});
return sum;
}
const result = sum([1, 3, 4, 5, 7], (n) => n % 2 !== 0);
console.log(result)
对函数的约束也可以使用类型别名
type Condition = (n: number) => boolean
接口可以继承
可以通过接口之间的继承,实现多种接口的组合。
interface A {
T1: string
}
interface B {
T2: number
}
interface C extends A, B {
T3: boolean
}
let u: C = {
T1: '1',
T2: 1,
T3: true
}
使用类型别名也可以实现继承,需要使用&,叫做交叉类型
type C {
T3: boolean
} & A & B
区别是子接口不能够覆盖父接口成员,会报错,而交叉类型会对子类型中同名变量的类型进行合并,如
type C {
T1: number
T3: boolean
} & A & B
let u: C = {
T1: '1', //T1类型为string和number,报错,会提示两种类型所有方法
T2: 1,
T3: true
}
readonly
只读修饰符,修饰的目标是只读,不在编译结果中
interface User {
readonly id: string
}
只能够赋初始值,后面不能更改
let arr: readonly number[] = [1, 2, 3];
arr = [2, 3, 4]
此时readonly修饰的是类型,此时数组的push pop等方法无效,而arr是可以改变的
interface User {
readonly arr: string []
}
let u:User = {
arr: ['s', 't']
}
u.arr.push('t');
//u.arr = ['t'];//报错
这种写法中,readonly修饰的是数组,不是类型,数组的地址是不能改变的。修饰对象中的属性只能用readonly,其他情况下使用const就可以了。
可以修饰数组和修饰类型一起使用,做到数组完全不能变。
类型兼容性
TS如何来判定两个类型是否兼容,可以实现互相赋值。
B -> A,如果能完成赋值,则B和A类型兼容。
TS使用鸭子辨型法(子结构辨别法):目标类型需要某一些特征,赋值的类型只要能满足某些特征即可。就像鸭子有会嘎嘎叫和会游泳两个特征。
以下是各种类型的规则
-
基本类型:完全匹配
-
对象类型:鸭子辨型法
interface Duck { sound: '嘎嘎' swin(): void } let person = { name: '扮演鸭子', sound: '嘎嘎嘎' as '嘎嘎嘎', //类型断言 swim(){ console.log('游泳') } } let duck: Duck = person; //可以赋值对象进行赋值时,只要符合某些特征就可以进行赋值
** 特殊情况** 直接使用字面量类型会报错,这本身也符合对Duck的类型约束
let duck: Duck = { name: '扮演鸭子', sound: '嘎嘎嘎' as '嘎嘎嘎', //类型断言 swim(){ console.log('游泳') } };只要右边是对象变量形式,都使用鸭子辨型法。 TS认为直接对
duck赋值时,认为开发者明确知道自己在做什么,使用更严格的判断,而使用对象进行赋值时,该对象可能来源于一个函数,类型约束变的宽松,比如一个后端接口 ,我们需要使用其中个别属性,后端不可能还要分成两个对象来传。 -
函数类型:一切无比自然
interface Condition {
(n: number): boolean
}
function sum(numbers: number[], callback: Condition): number {
let sum = 0;
numbers.forEach((n, i) => {
if (callback(n, i)) {
sum += n;
}
});
return sum;
}
const result = sum([1, 3, 4, 5, 7], (n) => n % 2 !== 0);
console.log(result)
参数:传递给目标函数的参数可以少,但不可以多
返回结果:要求返回时必须返回。类型要正确,不要求返回时随便,即使返回了也不会用到