TypeScript实战-13-TS的类型检查机制-各种类型之间的兼容性

一,前言

本篇只要讨论TS不同类型的兼容性
类型兼容经常发生在接口,函数和类中

二,类型兼容

如果类型A可以被赋值给类型B,那么就可以说类型B兼容类型A
如果 : B(目标类型) = A(源类型) ,	则 : 类型B兼容类型A

之前的例子中,我们就遇到过number和null的兼容问题:

在strictNullChecks = false时(默认true),字符串变量是可以被赋值为null类型的

tsconfig.json

...
"strictNullChecks": false,   
...

number和null的兼容
此时,number类型可以兼容null类型,也就是null类型是number类型的子类型

TS允许不同类型的变量之间相互赋值,从某种程度上产生了不可靠性,同时增加了TS语言的灵活性

三,接口的兼容性

测试接口之间的兼容性:

定义两个接口X和Y,X含有ab属性,Y含有abc属性
定义两个变量x和y,分别指定为X和Y的接口类型
将变量y赋值给变量x,测试接口X是否兼容接口Y
将变量x赋值给变量y,测试接口Y是否兼容接口X

接口兼容测试

可以看出:

源类型必须具备目标类型的全部必要属性,才可以被赋值

可以这样理解:

接口类型相当于对变量属性的约束,必须全部满足才可以兼容赋值

四,函数的兼容性

一般地,当两个函数相互赋值时,就发生函数兼容,
例如方法的参数为一个函数类型,当调用该方法传参时,就会发生函数兼容
此时,函数类型的这个参数就是目标类型,将要传入的参数为源类型

构造用于测试的样例:

首先,使用type关键字定义一个函数类型Handler(包含两个参数),
然后,使用此函数作为高阶函数foo的参数

高阶函数定义: 以函数作为参数的函数
// 使用type关键字定义一个函数类型(包含两个参数)
type Handler  =(a: number, b: number) => void
// 使用函数类型Handler作为高阶函数foo的参数
function foo(handler: Handler) {
    return handler
}

要想使目标函数能够兼容源函数,需同时满足以下三个条件:

1,参数的个数

目标函数的参数个数一定要多于源函数的参数个数

2,参数的类型

参数类型必须匹配

3,返回值的类型

目标函数的返回值类型 与 原函数的返回值类型 必须相同或为其子类型

五,函数兼容性的影响因素1-参数个数

目标函数的参数个数一定要多于源函数的参数个数

当参数固定时,多参,少参情况下的兼容性:
参数个数兼容
当参数不固定时,固定参数与可选参数,剩余参数的兼容性
参数不固定兼容
可以看到:

固定参数 可兼容 可选参数 和 剩余参数
可选参数 不兼容 固定参数 和 剩余参数(strictFunctionTypes = true时)
可选参数 可兼容 固定参数 和 剩余参数(strictFunctionTypes = false时)
剩余参数	可兼容 固定参数 和 可选参数

结论:

目标函数的参数个数一定要多于源函数的参数个数
即:在确保 目标函数参数个数 > 源函数参数个数时, 才可能兼容

可选参数 和 剩余参数不能保证一定被传入,所以不一定兼容(即严格模式下不兼容)

联想理解:

可以使用插线板插头进行联想理解
目前很多3相插座可同时支持2相插头和3相插头接入
此时,插座相当于"目标",插头相当于"源",

所以,我们说这种3相插座是可以兼容2相插头和3相插头的
而普通的2相插座是不能兼容3相插头的,因为3相插头多一个头...

六,函数兼容性的影响因素2-参数类型

参数类型必须匹配

基础类型:
参数类型对函数兼容性的影响
对象类型:
对象类型参数对函数兼容性的影响

同样可以使用插座和插头的记忆方式:参数多的兼容参数少的

也可以通过修改strictFunctionTypes配置项,可以使函数兼容

tsconfig.json

...
"strictFunctionTypes": true,   
...

函数参数的双向协变:

函数的参数之间可以相互的赋值的情况,叫函数参数的双向协变

七,函数兼容性的影响因素3-返回值类型

目标函数的返回值类型 与 源函数的返回值类型 必须相同或为其子类型

返回值类型对函数兼容性的影响
记忆方式:

返回值类型,和接口类似相当于是对函数的一种约束,所以必须在满足要求的前提先才可以兼容
即参数可以多了我不用,但绝对不可以少

八,函数重载对函数兼容性的影响

继续使用之间介绍函数重载的例子:

定义两个重载函数,并实现
此时,重载列表中的函数为目标函数,实现函数为源函数
// 函数重载对函数兼容性的影响

// 函数定义-重载列表
function overload(a: number, b: number): number
function overload(a: string, b: string): string
// 函数实现
function overload(a: any, b: any): any {}

从重载函数的运行机制分析重载函数的兼容性:

前面我们介绍过,重载函数的实现是一个类型较为宽泛的函数
运行时,TS编译器会按顺序去重载列表一一进行匹配,并使用第一个匹配的函数定义执行

所以,目标函数(重载列表中的函数)的参数个数 要大于等于 源函数(实现函数)的参数
返回值类型也要符合相应要求

兼容 : 目标函数参数个数 > 源函数参数个数
目标函数参数个数 > 源函数参数个数
不兼容 : 目标函数参数个数 < 源函数参数个数
目标函数参数个数<源函数参数个数
重载函数返回值类型不兼容:
重载函数返回值类型不兼容


九,枚举类型的兼容性

枚举类型和数值类型是可以完全互相兼容的

// 枚举类型的兼容性
enum EnumA {
    ItemA1,
    ItemA2
}
enum EnumB {
    ItemB1,
    ItemB2
}
let var1: EnumA.ItemA1 = 1
let var2: number = EnumA.ItemA1 

不同枚举之间不兼容:
不同枚举之间不兼容


十,类的兼容性

两个类是否兼容,仅比较结构,类似接口兼容

具有相同成员的两个类是相互兼容的:

注意:静态成员和构造函数不参与类的兼容性比较

类的兼容性
两个类中包含相同的私有成员时,两个类不兼容,但父类和子类相互兼容
包含私有成员的类之间的兼容性


十一,泛型的兼容性

泛型类型T被接口成员使用时,才会影响泛型的兼容性

泛型的兼容性

十二,泛型函数的兼容性

定义相同的两个泛型函数,如果未指定具体类型,则可相互兼容

泛型函数的兼容性

十三,结尾

TS允许类型兼容的变量之间相互进行赋值操作,这个特性增加了语言的灵活性

结构之间兼容:成员少的兼容成员多的
函数之间兼容:参数多的兼容参数少的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章