TS 是結構類型系統(structural type system),基於結構/形狀檢查類型,而非類型的名字。 TS 中的相容性,主要看**結構是否相容**。(核心是考慮安全性),結構化的類型系統(又稱鴨子類型檢查),如兩個類型名字不一樣但是無法區分 類型相容性是基於結構子類型的。 結構類型是一種只 ...
目錄
TS 是結構類型系統(structural type system),基於結構/形狀檢查類型,而非類型的名字。
TS 中的相容性,主要看結構是否相容
。(核心是考慮安全性),結構化的類型系統(又稱鴨子類型檢查),如兩個類型名字不一樣但是無法區分
類型相容性是基於結構子類型的。 結構類型是一種只使用其成員來描述類型的方式。
如果
x
要相容y
,那麼y
至少具有與x
相同的屬性。
這裡要檢查y
是否能賦值給x
,編譯器檢查x
中的每個屬性,看是否能在y
中也找到對應屬性。
X 相容 Y:X(目標類型)= Y(源類型)
簡單一句話概括相容性: 重新賦值不報錯(類型自動轉化)
一.基本數據類型的相容性
let temp: string | number;
let num!: number;
temp = num;
let obj: {
toString(): string;
};
let str: string = "yya";
obj = str; // 字元串中具備toString()方法,所以可以進行相容
obj.toString(); // 安全, 保證使用的時候不會發生異常
二.介面相容性
介面的相容性,只要滿足介面中所需要的類型即可!(保證你要的,我都有,就行,多了也沒關係)
interface IAnimal {
name: string;
age: number;
}
interface IPerson {
name: string;
age: number;
address: string;
}
let animal: IAnimal;
let person: IPerson = {
name: "yya",
age: 18,
address: "beijing",
};
type T2 = IPerson extends IAnimal ? true : false; // true
animal = person; // 子類賦予給父類 相容
三.函數的相容性
函數的相容性主要是比較參數和返回值
參數:賦值函數的參數要少於等於被賦值的函數:也就是說,對應函數的參數來講,少的參數可以賦予給多的
,因為內部實現傳了多個可以少用或不用(忽略額外的參數在 JavaScript 里是很常見的)
sum2
的每個參數必須能在sum1
里找到對應類型的參數。 註意的是參數的名字相同與否無所謂,只看它們的類型
。 sum2
的每個參數在sum1
中都能找到對應的參數,所以允許賦值。
let sum1 = (a: string, b: string) => a + b;
let sum2 = (a: string) => a;
sum1 = sum2;
舉例: Array#forEach
給回調函數傳 3 個參數:item,index 和 array。 儘管如此,傳入一個只使用第一個參數的回調函數也是可以的
type Func<T> = (item: T, index: number, array: any[]) => void;
function forEach<T>(arr: T[], cb: Func<T>) {
for (let i = 0; i < arr.length; i++) {
cb(arr[i], i, arr);
}
}
forEach([1, 2, 3], (item) => {
console.log(item);
});
返回值:
type sum1 = () => string | number;
type sum2 = () => string;
let fn1: sum1;
let fn2!: sum2;
fn1 = fn2;
四.類的相容性
類與對象字面量和介面差不多,但有一點不同:類有靜態部分和實例部分的類型。 比較兩個類類型的對象時,只有實例的成員會被比較。 靜態成員和構造函數不在比較的範圍內。
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
類的私有成員和受保護成員
只要有 private 或者 protected 關鍵字會影響相容性, 當檢查類實例的相容時,如果目標類型包含一個 private 私有成員,那麼源類型必須包含來自同一個類的這個私有成員
。 這條規則也適用於包含 protected 受保護成員實例的類型檢查。 允許子類賦值給父類,但是不能賦值給其它有同樣類型的類。
class A {
private name!: string;
age!: number;
}
class B {
private name!: string;
age!: number;
}
// let a: A = new B(); // error
class Parent {
protected name: string = "zf";
age: number = 11;
}
class Child extends Parent {}
let child: Parent = new Child(); // ok
五.泛型的相容性
泛型比較的是最終的結果 比較的不是泛型傳遞的參數
例一:
interface Empty<T> {}
let x: Empty<number>;
let y!: Empty<string>;
type xx = Empty<number> extends Empty<string> ? true : false; // true
x = y; // OK 因為 y 匹配 x 的結構
在例一中,x 和 y 是相容的,因為它們的結構使用類型參數時並沒有什麼不同。 把這個例子改變一下,增加一個成員,就能看出是如何工作的了:
例二:
interface NotEmpty<T> {
data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
type xx = NotEmpty<number> extends NotEmpty<string> ? true : false; // false
x = y; // Error, 不相容
對於沒指定泛型類型的泛型參數時,會把所有泛型參數當成 any 比較。 然後用結果類型進行比較,就像例一:
let identity = function <T>(x: T): T {};
let reverse = function <U>(y: U): U {};
identity = reverse; // OK, (x: any) => any 匹配 (y: any) => any
六.枚舉的相容性
枚舉類型與數字類型相容,並且數字類型與枚舉類型相容
enum Status {
Pending,
Resolved,
Rejected,
}
let current = Status.Pending;
let num = 0;
current = num;
num = current;
不同枚舉類型之間是不相容的。
enum Status {
Pending,
Resolved,
Rejected,
}
enum Color {
Red,
Blue,
Green,
}
let current = Status.Pending;
let color = Color.Red;
current = color; // 不能將類型“Color.Red”分配給類型“Status”
標稱類型簡短介紹
類型分為兩種 結構化類型(structural type system) 、標稱類型(nominal type system)
標稱類型: 雖然 BTC,USDT 都是 number 類型,但還是想要用不同的類型表示,且不能互換,數據的值本身沒什麼區別,安上不同名字就是不同類型,也就是說,標稱類型系統中,兩個變數是否類型相容(可以交換賦值)取決於這兩個變數顯式聲明的類型名字是否相同。
class AddType<S> {
private _type!: S;
}
type NewType<T, S extends string> = T & AddType<S>;
type BTC = NewType<number, "btc">; // number + BTC
type USDT = NewType<number, "usdt">; // number + USDT
let btc = 100 as BTC;
let usdt = 100 as USDT;
function getCount(count: USDT) {
return count;
}
getCount(usdt);