ts的類型檢查

一.類型推論

1.通用類型(從右到左推斷):
TypeScript裏的類型推論。即,類型是在哪裏如何被推斷的。

在有些沒有明確指出類型的地方,類型推論會幫助提供類型,如下面的例子中的a,b,c
變量a被推斷爲number,b被推斷爲any[],c被推斷爲strng[],
當需要從幾個表達式中推斷類型時候,會使用這些表達式的類型來推斷出一個最合適的通用類型,例如下例中的x
由於x有多個類型,所以x被推斷爲number|null.
設置函數默認參數或者確定函數返回值的時候,也可以做出推斷出,如下例中的xx.
有明確的number參數,和string返回值

2.上下文類型(從左到右推斷):
類型推論也可能按照相反的方向進行。按上下文歸類會發生在表達式的類型與所處的位置相關時。比如:

window.onmousedown = function(mouseEvent) {
    console.log(mouseEvent.button);  //<- Error
};
/*
上面這個例子會得到一個類型錯誤,TypeScript類型檢查器使用Window.onmousedown函數的類型來推斷右邊函數表
達式的類型。 因此,就能推斷出 mouseEvent參數的類型了。 如果函數表達式不是在上下文類型的位置, 
mouseEvent參數的類型需要指定爲any,這樣也不會報錯了,如下
*/

window.onmousedown = function(mouseEvent: any) {
    console.log(mouseEvent.button);  //<- Now, no error is given
};

二.類型斷言

// 定義一個空對象,然後給空對象添加一個button屬性
let dy = {};
dy.button = 'no btn'; // 報錯: 類型“{}”上不存在屬性“button”。

// 我們看到報錯了,
// 那麼怎麼解決這個報錯呢,這時候我們可以寫一個接口,讓接口有一個button屬性,然後使用斷言改寫上面的代碼

interface Dy {
    button:string
}
let dy = {} as Dy
dy.button = 'no btn';

// 但是這樣寫又一個弊端,不能束縛dy裏面一定存在button,
// 所以在不確定的情況下,少用這種斷言,那麼我們要怎麼寫呢,在聲明的時候就指定類型

let dy:Dy = {button:''}; // 確保dy裏面一定受Dy的束縛
dy.button = 'no btn';

 

三.類型兼容

結構之間的兼容: 成員少的兼容成員多的
函數之間的兼容:參數多的兼容參數少的

什麼是類型兼容?
當一個類型y可以被賦值給類型x時,我們可以說類型x兼容類型y
x兼容y: x(目標類型) = y(源類型)

接口兼容

interface XX {
    a: number;
    b: number
}
interface YY {
    a: number;
    b: number;
    c: number
}
var x: XX = { a: 1, b: 2 };
var y: YY = { a: 5, b: 6, c: 3 }
x = y; // 正確
y = x; //報錯:  “c”需要在這裏聲明。

這是什麼原理呢,因爲y對於x來說,y滿足了x的必需屬性,但是x對y來說缺少了c這個必需屬性,這個被稱做“鴨式辨型法”或“結構性子類型化”, 所以x可以兼容y,而y並不兼容x,總結來說就是:成員少的會兼容成員多的

函數兼容

查看兩個函數是否兼容,通常看兩個函數是否相互賦值

函數作爲參數的情況下

type handler = (x:number,y:number) => void; 
function fa(handler){
    return handler;
}

//參數個數:目標函數的參數個數一定要多於原函數的參數個數
let heanler1 = (x: number) => { };
fa(heanler1); // 正確

let heanler2 = (x: number,y:number,z:number) => { };
fa(heanler2); // 報錯

 

可選參數和剩餘參數之間的兼容性

固定參數可以兼容可選參數和剩餘參數
可選參數不兼容固定參數和剩餘參數

let a = (a:number,b:number)=>{};
let b = (a?:number,b?:number)=>{};
let c = (...arg:number[])=>{};
// 固定參數可以兼容可選參數和剩餘參數即
a=b;a=c; // 正確

// 可選參數不兼容固定參數和剩餘參數
// 設置"strictFunctionTypes": false,就可以跳過下面的報錯
b=a;// 報錯
b=c;// 報錯

參數類型

參數成員多兼容參數成員少的,跟接口兼容正好相反

let handler3 = (x:string)=>{}
fa(handler3); // 參數類型錯誤的話也會報錯
// 對象參數
interface Point3D{
    x:number;
    y:number;
    z:number;
}
interface Point2D{
    x:number;
    y:number;
}

let p3d = (p:Point3D) => {}
let p2d = (p:Point2D) => {}

// 參數成員多兼容參數成員少的,跟接口兼容正好相反
p3d = p2d;
p2d = p3d; // 報錯

返回值類型

返回值成員少的兼容返回值成員多的

let h = ()=> ({name:'張三'})
let g = ()=> ({name:'張三',age:'30'})
h=g;
g=h; // 報錯   {name:'張三'}不能分配給{name:'張三',age:'30'}

函數重載的兼容性

對於有重載的函數,源函數的每個重載都要在目標函數上找到對應的函數簽名。 這確保了目標函數可以在所有源函數可調用的地方調用。

目標函數的參數,要多於原函數的參數,並且返回值的類型也要符合相應的要求

function overload(a:number,b:number):number; // 目標函數
function overload(a:string,b:string):string;
function overload(a:any,b:any):any{}; // 原函數
// function overload(a:any,b:any):{}; // 報錯
// function overload(a:any,b:any,c:any):any{}; // 報錯

枚舉的兼容性

枚舉類型與數字類型兼容,並且數字類型與枚舉類型兼容。不同枚舉類型之間是不兼容的。比如,

enum Status { Ready, Waiting };
enum Color { Red, Blue, Green };

let st = Status.Ready; // st:Status.Ready

// 枚舉和數字之間是完全兼容的
st = 2;  // 正確  
let st1:

// 枚舉之間是不兼容的
st = Color.Green;  // 報錯  
st = Status.Waiting;  // 報錯

類的兼容性

類與對象字面量和接口差不多,但有一點不同:類有靜態部分和實例部分的類型。 比較兩個類類型的對象時,只有實例的成員會被比較。 靜態成員和構造函數不在比較的範圍內。如果類中存在私有成員,則不兼容

類的私有成員和受保護成員
類的私有成員和受保護成員會影響兼容性。 當檢查類實例的兼容時,如果目標類型包含一個私有成員,那麼源類型必須包含來自同一個類的這個私有成員。 同樣地,這條規則也適用於包含受保護成員實例的類型檢查。 這允許子類賦值給父類,但是不能賦值給其它有同樣類型的類。

class Animal {
    feet: number;
    constructor(name: string, numFeet: number) { }
}
class Size {
    feet: number;
    constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s;  // OK
s = a;  // OK

// 還是上面的代碼,Animal 添加私有成員
class Animal {
    ...
    private age: number; // 添加私有成員後
}
...
a = s;  // Error
s = a;  // OK

// 繼給Animal添加私有成員後,再給Size添加私有成員
class Size {
     ...
     private age: number; // Size 添加私有成員後
}
...
a = s;  // Error
s = a;  // Error

// 允許子類賦值給父類
class Dog extends Animal { }
let c = new Dog('哈士奇', 20);
c = a; // OK
a = c; // OK

泛型的兼容性

因爲TypeScript是結構性的類型系統,類型參數隻影響使用其做爲類型一部分的結果類型。比如,

interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;

x = y;  // OK, 因爲y符合x的結構

上面代碼裏,xy是兼容的,因爲它們的結構使用類型參數時並沒有什麼不同。 把這個例子改變一下,增加一個成員,就能看出是如何工作的了

// 當T被使用的時候,就會報錯
interface NotEmpty<T> {
    data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;

x = y;  // Error, 因爲x和y不兼容

如果兩個泛型函數定義相同,並且沒有指定類型參數,那麼他們是可以相互兼容的,例如

let identity = function <T>(x: T): T {
    console.log(x)
    return x;
}

let reverse = function <U>(y: U): U {
    console.log(y)
   return y;
}
identity = reverse;  // OK, because (x: any) => any matches (y: any) => any

而下面的代碼則是會報錯

let identity = function <T>(x: T): T {
    console.log(x)
    return x;
}

let reverse = function <U,T>(y: U,z:T): U {
    console.log(y)
   return y;
}

identity = reverse;  // Error

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章