typescript中的结构及类型系统
2022-12-04 本文已影响0人
成熟稳重的李先生
- 接口的兼容性
- 如果传入的变量和声明的类型不匹配,TS就会进行兼容性检查
- 原理是
Duck-Check
(一个对象,只要它会鸭子叫,那就当它是鸭子),就是说只要目标类型中声明的属性变量在源类型中都存在就是兼容的
interface Animal {
name: string;
age: number;
}
interface Person {
name: string;
age: number;
gender: number
}
// 要判断目标类型`Person`是否能够兼容输入的源类型`Animal`
function getName(animal: Animal): string {
return animal.name;
}
let p = {
name: 'zhufeng',
age: 10,
gender: 0
}
getName(p); // 不报错,因为p有name和age属性
//只有在传参的时候两个变量之间才会进行兼容性的比较,赋值的时候并不会比较,会直接报错
let a: Animal = { //不报错,原因同上
name: 'zhufeng',
age: 10,
gender: 0
}
- 基本类型的兼容性
- 基本类型,如string,number等等,在调用它们自身的方法时,会隐式的将其变成对应的包装类型,用完销毁。因此在调用string.toString(),string.length,number.toFixed(2)时,能正常拿到结果
let num: string | number;
let str: string = "zhufeng";
num = str;
let num2: {
toString(): string
}
let str2: string = "abc";
num2 = str2; //这里不报错的原因如上
- 类的兼容性
- 在TS中是结构类型系统,只会对比结构而不在意类型(只看是否有某一方法或者属性,并不判断类型)
class Animal {
name: string
}
class Bird extends Animal {
swing: number
}
let a: Animal;
a = new Bird(); //赋值成功,因为Bird实例中有name属性
let b: Bird;
b = new Animal(); //赋值失败,Animal实例中没有swing属性
// 如果子类没有自己的属性(即父子拥有相同属性)
class Tiger extends Animal {}
let c: Tiger;
c = new Animal(); //赋值成功,因为tiger只有name属性,而Animal实例中有此属性
// 甚至两个没关系的类,只要属性相同,便能赋值
class Person {
name: string
}
let p: Person;
p = new Animal()
- 函数的兼容性
- 比较函数的时候是要先比较函数的参数,再比较函数的返回值
4.1 比较参数
type sumFunc = (a:number,b:number)=>number;
let sum:sumFunc;
function f1(a:number,b:number):number{
return a+b;
}
sum = f1;
//可以省略一个参数
function f2(a:number):number{
return a;
}
sum = f2;
//可以省略全部参数
function f3():number{
return 0;
}
sum = f3;
//多一个参数就不行了
function f4(a:number,b:number,c:number){
return a+b+c;
}
sum = f4;
4.2 比较返回值
type GetPerson = ()=>{name:string,age:number};
let getPerson:GetPerson;
//返回值一样可以
function g1(){
return {name:'lc',age:10};
}
getPerson = g1;
//返回值多一个属性也可以
function g2(){
return {name:'lc',age:10,gender:'male'};
}
getPerson = g2;
//返回值少一个属性可不行
function g3(){
return {name:'lc'};
}
getPerson = g3;
//因为有可能要调用返回值上的方法
getPerson().age.toFixed();
- 函数参数的协变
- 当比较函数参数类型时,只有当源函数参数能够赋值给目标函数或者反过来时才能赋值成功
- 需要将
tsconfig.json
中的"strictFunctionTypes"
设置为false
let sourceFunc = (args: number | string) => { }
let target1Func = (args: number | string) => { }
let target2Func = (args: number | string | boolean) => { }
sourceFunc = target1Func;
sourceFunc = target2Func;
- 接口的兼容性
- 接口和类的兼容性类似,a接口包含b接口,那么a就可以赋值给b
enum EventType {
MouseEvent
}
interface Event {
timestamp: number
}
interface MouseEvent extends Event {
pageX: string,
pageY: string
}
function addEventListener(eventType: EventType, handler: (event: Event) => void){
}
// MouseEvent可以赋值给event类型
addEventListener(EventType.MouseEvent, (event: MouseEvent) => {
})
- 泛型的兼容性
- 泛型在判断兼容性的时候会先判断具体的类型,然后再进行兼容性判断
//接口内容为空没用到泛型的时候是可以的
//1.接口内容为空没用到泛型的时候是可以的
interface Empty<T>{}
let x!:Empty<string>;
let y!:Empty<number>;
x = y; //成功
相当于 x = {}, y = {},因此,赋值成功
//2.接口内容不为空的时候不可以
interface NotEmpty<T>{
data:T
}
let x1!:NotEmpty<string>;
let y1!:NotEmpty<number>;
x1 = y1;// 报错
//相当于 x1 = { data:string=undefined }, y1 = {data:number = undefined}
//实现原理如下,称判断具体的类型再判断兼容性
interface NotEmptyString{
data:string
}
interface NotEmptyNumber{
data:number
}
let xx2!:NotEmptyString;
let yy2!:NotEmptyNumber;
xx2 = yy2;
let xx3!:NotEmpty<number>;
let yy3!:NotEmpty<number | string>;
xx3 = yy3; //报错
yy3 = xx3; //成功
- 枚举的兼容性
- 枚举类型与数字类型兼容,并且数字类型与枚举类型兼容
- 不同枚举类型之间是不兼容的
//数字可以赋给枚举
enum Colors {Red,Yellow}
let c:Colors;
c = Colors.Red;
c = 1; //成功
c = '1'; //报错
//枚举值可以赋给数字
let n:number;
n = 1;
n = Colors.Red;
enum specialColos{Red, Yellow}
c = specialColos.Red //报错,不同枚举之间不能相互赋值