一.類型推論
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的結構
上面代碼裏,x
和y
是兼容的,因爲它們的結構使用類型參數時並沒有什麼不同。 把這個例子改變一下,增加一個成員,就能看出是如何工作的了
// 當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