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允許類型兼容的變量之間相互進行賦值操作,這個特性增加了語言的靈活性

結構之間兼容:成員少的兼容成員多的
函數之間兼容:參數多的兼容參數少的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章