欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

TypeScript中類型兼容性的示例詳解

 更新時間:2022年08月22日 09:51:02   作者:CUGGZ  
JavaScript是一門弱類型語言,它對類型是弱校驗,所以才有了TypeScript。本文就來和大家一起看看TypeScript的類型兼容性的概念和分類,需要的可以參考一下

JavaScript 是一門弱類型語言,它對類型是弱校驗,正因為這個特點,所以才有了TypeScript這個強類型語言系統(tǒng)的出現,來彌補類型檢查的短板。TypeScript在實現類型強校驗的同時,還要滿足 JavaScript 靈活的特點,所以就有了類型兼容性這個概念。了解類型兼容性可以避免在實際的開發(fā)中出現一些低級錯誤。下面就來看看類型兼容性的概念和分類。

1. 類型兼容性的概念

所謂的類型兼容性用于確定一個類型是否能賦值給其他類型。TypeScript中的類型兼容性是基于結構類型的,結構類型是一種只使用其成員來描述類型的方式。其基本原則是,如果 x 要兼容 y,那么 y 至少要具有與 x 相同的屬性。

下面來看一個例子,構建一個 Teacher 類 ,然后聲明一個接口 Student,Student 的屬性 Teacher 都有,而且還多了其他的屬性,這種情況下 Student 就兼容了 Teacher

class?Teacher?{
????constructor(public?weight:?number,?public?name:?string,?public?job:?string)?{
????
????}
}</code><code>interface?Student?{
????name:?string
????weight:?number
}</code><code>let?x:?Student;
x?=?new?Teacher(120,?'TS',?'teacher')?//??

如果反過來,Teacher 并沒有兼容 Student,因為 Student 的屬性比 Person 少一個。

2. 特殊類型的類型兼容性

先來看看 TypeScript 中一些特殊類型的類型兼容性。

(1)any

any 類型可以賦值給除了 never 之外的任意其他類型,反過來其他類型也可以賦值給 any。也就是說 any 可以兼容除了 never 之外的所有類型,同時也可以被所有的類型兼容。

let?any:?any;

let?a:?number?=?any;???????//??
let?b:?{}?=?any;???????????//??
let?b:?()?=>?number?=?any;?//??

(2)never

never 類型可以賦值給任何其他類型,但不能被其他任何類型賦值。

let?never:?never?=?(()?=>?{
??throw?Error('never');
})();

let?a:?number?=?never;???????//??
let?b:?()?=>?number?=?never;?//??
let?c:?{}?=?never;???????????//??

可以看到,這里將 never 類型賦值給了 number、函數、對象類型,都是沒有問題的。

(3)unknown

unknown 和 never 的特性是相反的,即不能把 unknown 賦值給除了 any 之外的任何其他類型,但其他類型都可以賦值給 unknown。

let?unknown:?unknown;

const?a:?number?=?unknown;???????//?不能將類型“unknown”分配給類型“number”。
const?b:?()?=>?number?=?unknown;?//?不能將類型“unknown”分配給類型“()?=> number”。
const?c:?{}?=?unknown;???????????//?不能將類型“unknown”分配給類型“{}”。

可以看到,當把 unknown 類型賦值給 number、函數、對象類型時,都報錯了,這就是因為類型之間不能兼容。

3. 函數類型的類型兼容性

函數的類型兼容性主要包括以下六個方面:

(1)參數數量

函數參數數量要想兼容,需要滿足一個要求:如果將函數 y 賦值為 x,那么要求 x 中的每個參數都應在 y 中有對應,也就是 x 的參數個數小于等于 y 的參數個數

let?x?=?(a:?number)?=>?0;
let?y?=?(b:?number,?c:?string)?=>?0;

上面定義的兩個函數,如果進行賦值的話,來看下兩種情況的結果:

y?=?x;??//??

將 x 賦值給 y 是可以的,因為 x 的參數個數小于等于 y 的參數個數,而至于參數名是否相同是無所謂的。

而將 y 賦值給 x 就不可以了:

x?=?y;?//?不能將類型“(b: number, c: string)?=> number”分配給類型“(a: number)?=> number”。

這里 y 的參數個數要大于 x,所以報錯了。

(2)函數參數類型

除了參數數量,參數的類型也需要對應:

let?x?=?(a:?number)?=>?0;
let?y?=?(b:?string)?=>?0;
x?=?y;?// error 不能將類型“(b: string)?=> number”分配給類型“(a: number)?=> number”。

可以看到,x 和 y 兩個函數的參數個數和返回值都相同,只是參數類型對不上,所以也是不行的。

(3)剩余參數和可選參數

當要被賦值的函數參數中包含剩余參數(…args)時,賦值的函數可以用任意個數參數代替,但是類型需要對應:

const?getNum?=?(
??arr:?number[],
??callback:?(...args:?number[])?=>?number
):?number?=>?{
??return?callback(...arr);
};

getNum(
??[1,?2],
??(...args:?number[]):?number?=>?args.length?//?返回參數的個數
);

剩余參數其實可以看做無數個可選參數,所以在兼容性方面是差不多的。

再來看一個可選參數和剩余參數結合的例子:

//?第二個參數callback是一個函數,函數的第二個參數為可選參數
const?getNum?=?(
??arr:?number[],
??callback:?(arg1:?number,?arg2?:?number)?=>?number?
):?number?=>?{
??return?callback(...arr);?//?error?應有?1-2?個參數,但獲得的數量大于等于?0
};

這里因為 arr 可能為空數組,如果為空數組則…arr不會給callback傳入任何實際參數,所以這里就會報錯。如果換成return callback(arr[0], …arr)就沒問題了。

(4)參數雙向協變

函數參數雙向協變即參數類型無需絕對相同

let?funcA?=?function(arg:?number?|?string):?void?{};
let?funcB?=?function(arg:?number):?void?{};
//?funcA?=?funcB?和?funcB?=?funcA都可以

這里 funcA 和 funcB 的參數類型并不完全一樣,funcA 的參數類型為一個聯合類型 number | string,而 funcB 的參數類型為 number | string 中的 number,這兩個函數也是兼容的。

(5)返回值類型

函數返回值的類型也是要對應的:

let?x?=?(a:?number):?string?|?number?=>?0;
let?y?=?(b:?number)?=>?"a";
let?z?=?(c:?number)?=>?false;
x?=?y;?//??
x?=?z;?//?不能將類型“(c:?number)?=>?boolean”分配給類型“(a:?number)?=>?string?|?number”

這里 x 函數的返回值是聯合類型,既可以是 string 類型也可以是 number 類型。而 y 的返回值類型是 number 類型,參數個數和類型也沒問題,所以可以賦值給 x。而 z 的返回值類型 false 并不是 string 也不是 number,所以不能賦值。

(6)函數重載

帶有重載的函數,要求被賦值的函數的每個重載都能在用來賦值的函數上找到對應的簽名:

function?merge(arg1:?number,?arg2:?number):?number;?//?merge函數重載的一部分
function?merge(arg1:?string,?arg2:?string):?string;?//?merge函數重載的一部分
function?merge(arg1:?any,?arg2:?any)?{?//?merge函數實體
??return?arg1?+?arg2;
}
function?sum(arg1:?number,?arg2:?number):?number;?//?sum函數重載的一部分
function?sum(arg1:?any,?arg2:?any):?any?{?//?sum函數實體
??return?arg1?+?arg2;
}
let?func?=?merge;
func?=?sum;?//?error?不能將類型“(arg1:?number,?arg2:?number)?=>?number”分配給類型“{?(arg1:?number,?arg2:?number):?number;?(arg1:?string,?arg2:?string):?string;?}”

sum 函數的重載缺少參數都為string返回值為string的情況,與merge函數不兼容,所以賦值時就會報錯。

4. 枚舉的類型兼容性

數字枚舉成員類型與數字類型是互相兼容的:

enum?Status?{
??On,
??Off
}
let?s?=?Status.On;
s?=?1;
s?=?3;

雖然 Status.On 的值是 0,但是因為數字枚舉成員類型和數值類型是互相兼容的,所以這里給s賦值為 3 是沒問題的。但是不同枚舉值之間是不兼容的:

enum?Status?{
??On,
??Off
}
enum?Color?{
??White,
??Black
}
let?s?=?Status.On;
s?=?Color.White;?//?不能將類型“Color.White”分配給類型“Status”。

雖然 Status.On 和 Color.White 的值都是 0,但它們是不兼容的。

字符串枚舉成員類型和字符串類型是不兼容的:

enum?Status?{
??On?=?'on',
??Off?=?'off'
}
let?s?=?Status.On
s?=?'TypeScript'?//?不能將類型"TypeScript"分配給類型“Status”

這里會報錯,因為字符串字面量類型'TypeScript'Status.On是不兼容的。

4. 類類型的類型兼容性

比較兩個類的類型兼容性時,只有實例成員和方法會相比較,類的靜態(tài)成員和構造函數不進行比較

class?Animal?{
??static?age:?number;
??constructor(public?name:?string)?{}
}

class?People?{
??static?age:?string;
??constructor(public?name:?string)?{}
}

class?Food?{
??constructor(public?name:?number)?{}
}

let?a:?Animal;
let?p:?People;
let?f:?Food;
a?=?p;?//?ok
a?=?f;?//?不能將類型“Food”分配給類型“Animal”。

Animal類和People類都有一個age靜態(tài)屬性,它們都定義了實例屬性name,類型是string。把類型為Peoplep賦值給類型為Animala是沒有問題的,因為類類型比較兼容性時,只比較實例的成員,這兩個變量雖然類型是不同的類類型,但是它們都有相同字段和類型的實例屬性name,而類的靜態(tài)成員是不影響兼容性的,所以它倆時兼容的。而類Food定義了一個實例屬性name,類型為number,所以類型為Foodf與類型為Animala類型是不兼容的,不能賦值。

類的私有成員和受保護成員:

類的私有成員和受保護成員會影響類的兼容性。當檢查類的實例兼容性時,如果目標(要被賦值的那個值)類型(這里實例類型就是創(chuàng)建它的類)包含一個私有成員,那么源(用來賦值的值)類型必須包含來自同一個類的這個私有成員,這就允許子類賦值給父類:

class?Parent?{
??private?age:?number;
??constructor()?{}
}

class?Children?extends?Parent?{
??constructor()?{
????super();
??}
}

class?Other?{
??private?age:?number;
??constructor()?{}
}

const?children:?Parent?=?new?Children();
const?other:?Parent?=?new?Other();?//?不能將類型“Other”分配給類型“Parent”。類型具有私有屬性“age”的單獨聲明

當指定 other 為 Parent 類類型,給 other 賦值 Other 創(chuàng)建的實例的時候,會報錯。因為 Parent 的 age 屬性是私有成員,外面是無法訪問到的,所以會類型不兼容。而children的類型我們指定為了Parent類類型,然后給它賦值為Children類的實例,沒有問題,是因為Children類繼承Parent類,且實例屬性沒有差異,Parent類有私有屬性age,但是因為Children類繼承了Parent類,所以可以賦值。

同樣,使用 protected 受保護修飾符修飾的屬性,也是一樣的:

class?Parent?{
??protected?age:?number;
??constructor()?{}
}

class?Children?extends?Parent?{
??constructor()?{
????super();
??}
}

class?Other?{
??protected?age:?number;
??constructor()?{}
}

const?children:?Parent?=?new?Children();
const?other:?Parent?=?new?Other();?//?不能將類型“Other”分配給類型“Parent”。屬性“age”受保護,但類型“Other”并不是從“Parent”派生的類

6. 泛型類型兼容性

泛型中包含類型參數,這個類型參數可能是任何類型,使用時類型參數會被指定為特定的類型,而這個類型只影響使用了類型參數的部分:

interface?Data<T>?{}

let?data1:?Data<number>;
let?data2:?Data<string>;
data1?=?data2;?//??

data1 和 data2 都是 Data 接口的實現,但是指定的泛型參數的類型不同,TS 是結構性類型系統(tǒng),所以上面將 data2 賦值給 data1 是兼容的,因為 data2 指定了類型參數為 string 類型,但是接口里沒有用到參數 T,所以傳入 string 類型還是傳入 number 類型并沒有影響。

再來看個例子:

interface?Data<T>?{
??data:?T;
}

let?data1:?Data<number>;
let?data2:?Data<string>;
data1?=?data2;?//?不能將類型“Data<string>”分配給類型“Data<number>”。不能將類型“string”分配給類型“number”

現在結果就不一樣了,賦值時報錯,因為 data1 和 data2 傳入的泛型參數類型不同,生成的結果結構是不兼容的。

以上就是TypeScript中類型兼容性的示例詳解的詳細內容,更多關于TypeScript類型兼容性的資料請關注腳本之家其它相關文章!

相關文章

最新評論