1.&:交叉類型(取所有類型的並集)
必須同時滿足這倆接口裏面的成員屬性
interface DogInterface{
run():void;
}
interface CatInterface{
jump():void;
}
const pet: DogInterface & CatInterface={
run(){},
jump(){}
}
2.聯合類型:
聲明的類型並不確定,可以爲多個類型中的一個
let age:string|number = '10'; // 可以爲字符串
let age1:string|number = 10; // 也可以爲數字
自變量類型
有的時候我們不僅需要限定一個變量的類型,而且還要限定變量的取值,在某一個特定的範圍內
// 自變量的聯合類型
let age2: 10|20|30 = 10;
對象的聯合類型
在類型不確定的情況下,只能訪問所有類型的公有成員,即:取所有類型成員的交集
interface DogInterface{ run():void; }
interface CatInterface{ jump():void; }
class Dogg implements DogInterface{
run(){}
eat(){}
}
class Catt implements CatInterface{
jump(){}
eat(){}
}
enum Types{Dog,Cat}
function getAnimal(type:Types){
// 此處animal的類型爲 animal: Dogg | Catt
const animal = Types.Dog === type?new Dogg():new Catt();
animal.eat(); //此時只能訪問eat方法,因爲eat是Dogg | Catt都有的方法.
// animal.run(); //Error 類型“Dogg | Catt”上不存在屬性“run”。
return animal;
}
可區分的聯合類型:結合了聯合類型和自變量類型的一種類型保護方法
一個類型如果是多個類型的聯合類型,並且 每個類型之間有一個公共的屬性,那麼我們就可以憑藉這個公共屬性,創建不同得類型保護區塊,看下面的例子
interface SquareInterface{
types:'Square';
width:number
}
interface CircularInterface{
types:'circular';
radius:number
}
type Shape = SquareInterface | CircularInterface; // 聯合類型
function getArea(s:Shape){
switch(s.types){ // 通過兩個類型的通用屬性type,來創建不同的類型保護區塊
case 'Square':
return s.width * s.width; // 在這可以訪問SquareInterface
case 'circular':
return s.radius * s.radius * 3.14; // 在這可以訪問CircularInterface
}
}
let ss: SquareInterface = {
types: 'Square',
width:100
}
getArea(ss); // 10000
上面的案例如果添加一種新的保護區塊會怎麼樣呢
// 添加一個長方形
interface RectangleInterface{
types:'rectangle';
width: number;
height:number
}
// 修改聯合類型
type Shape = SquareInterface | CircularInterface | RectangleInterface;
// 使用
getArea( {
types: 'rectangle',
width:100, height:100
});
//結果: undefined
很顯然上面的getArea(...)的結果是undefined,那麼要怎麼得到錯誤提示呢,有兩種方法
1.給方法確定返回值,如果getArea()返回undefined,則不能滿足;
function getArea(s:Shape):number{
...
}
此時給Shape新增聯合類型的時候,由於getArea沒有對應的方法,所以number則會提示報錯
2.添加never
function getArea(s:Shape){
switch(s.types){
...
default:
// 檢查s是不是never類型,如果s是never類型,則前面所有的分支全部被覆蓋,這個分支就不會走到, 如果是不是never類型,則前面的分支就遺漏掉
return ((e: never) => { throw new Error(e) })(s)
}
}
此時再給getArea傳入不滿足要求的參數,則會拋出一個錯誤信息
3.索引類型
先看一個例子
const obj = { a:1,b:2,c:3 }
function getArr(obj:any,keys:string[]){
return keys.map(key=>obj[key])
}
console.log(getArr(obj,['b','c'])); // [2,3]
console.log(getArr(obj,['r','s'])); // [undefined,undefined]
當keys裏面傳了obj裏面並不存在的數據是得到undefined,此時並沒有報錯.那麼ts要怎麼對這種類型進行約束呢?此時就要用到索引類型
索引類型的操作符
keyof T 表示 類型T的所有公共屬性的自變量的聯合類型
T[K] 類型T的屬性K代表的類型
T extends U 泛型變量可以通過繼承某個類型或者某個屬性
接下來我們利用索引類型來改造上面的代碼,就不會出現
const obj = { a:1,b:2,c:3 }
function getArr<T,K extends keyof T>(obj:T,keys:K[]):T[K][]{
return keys.map(key=>obj[key])
}
console.log(getArr(obj,['r','s']));
// 此時編輯器會報錯。Type 'string' is not assignable to type '"a" | "b" | "c"'.
4.映射類型
可以從一箇舊的類型生成一個新的類型,例如,我們有如下一個接口,是可以隨意修改的。
interface Obj { a:number;b:string}
let obj1: Obj = {a:1,b:'d'}
obj1.a = 3; // OK
然後,我們看下,怎麼把上面這個接口變成只讀或者可選呢的呢,如下代碼
// 設置只讀:模擬Readonly<T>
type ReadonlyNew<T> = {
readonly [P in keyof T]:T[P] // 索引出T中的所有元素(相當於for循環),設置成readonly,然後原樣返回
}
type ReadonlyObj = ReadonlyNew<Obj>; // 只讀
let obj2: ReadonlyObj = {a:2,b:'c'};
obj2.a = 3; // ERROR Cannot assign to 'a' because it is a constant or a read-only property.
//設置爲可選: 模擬Partial
type PartialNew<T> = {
readonly [P in keyof T]?:T[P]
}
type PartialObj = PartialNew<Obj>; // 可選
let obj3: PartialObj = { a: 2 } //OK
let obj4: Obj = { a: 2 } //Error
此時眼尖的同學看出來了,ReadonlyNew/PartialNew就是和TS內置的Readonly/Partial方法。
此外再介紹一種抽取Object子集的方法Pick,模擬Pick
interface Obj1 { a:number;b:number;c:number;d:number;}
type PickNew<T,K extends keyof T> = { // T中滿足K的被保留了下來
[P in K]:T[P] // 從K中選取,然後從T中返回
}
type PcikObj = PickNew<Obj1, 'a'|'d'>; // 此時PcikObj裏面就只有a和d兩項了
以上三種類型Readonly/Partial/Pick,官方有個稱號,叫同態,意味着只能作用於object屬性,而不會引入新的屬性
那麼非同態是什麼樣的呢,我們來看下Record
interface Obj1 { a: number; b: number; c: number; d: number;}
type RecordObj = Record<'x'|'y',Obj1>
// 此時可以看到RecordObj是這樣的
RecordObj = {
x: Obj1;
y: Obj1;
}
5.條件類型
T extends U ? X : Y 這一段是什麼意思呢?
解:如果類型T可以被賦值給類型U,那麼結果就是X類型,否則就是Y類型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends Function ? "function" :
T extends undefined ? "undefined" :
"object"
let t1: TypeName<string> = 'string';
type T2 = TypeName<number>; // type T2 = "number"
(A|B) extends U ? X : Y 可拆解成 (A extends U ? X : Y ) | (B extends U ? X : Y )
type t3 = TypeName<string | number>; // type t3 = "string" | "number"
根據這個特性可以實現一些類型的過濾,舉個例子
type Diff<U,K> = U extends K ? never : U;
type dif = Diff<"a" | "b" | "c", "a" | "e">;
// 結果: type dif = "b" | "c";
// (A|B) extends U ? X : Y 可拆解成 (A extends U ? X : Y ) | (B extends U ? X : Y )
// 根據上面的特性將Diff<"a" | "b" | "c", "a" | "e">拆卸成如下:
type _dif1 = Diff<"a", "a" | "e">; // never
type _dif2 = Diff<"b", "a" | "e">; // b
type _dif3 = Diff<"c", "a" | "e">; // c
type _dif = _dif1 | _dif2 | _dif3; // type _dif = "b" | "c";結果同diff
Diff的擴展,// 過濾掉某些類型
type NotNull<T> = Diff<T, undefined | null>;
type ts = NotNull<string | number | null | undefined>; // string|number
這些方法官方同樣的有內置方法,對應的爲
Exclude<T, U>
-- 從T
中剔除可以賦值給U
的類型。Extract<T, U>
-- 提取T
中可以賦值給U
的類型。NonNullable<T>
-- 從T
中剔除null
和undefined
。ReturnType<T>
-- 獲取函數返回值類型。InstanceType<T>
-- 獲取構造函數類型的實例類型。
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T02 = Exclude<string | number | (() => void), Function>; // string | number
type T03 = Extract<string | number | (() => void), Function>; // () => void
type T04 = NonNullable<string | number | undefined>; // string | number
type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[]
function f1(s: string) {
return { a: 1, b: s };
}
class C {
x = 0;
y = 0;
}
type T10 = ReturnType<() => string>; // string
type T11 = ReturnType<(s: string) => void>; // void
type T12 = ReturnType<(<T>() => T)>; // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[]
type T14 = ReturnType<typeof f1>; // { a: number, b: string }
type T15 = ReturnType<any>; // any
type T16 = ReturnType<never>; // any
type T17 = ReturnType<string>; // Error
type T18 = ReturnType<Function>; // Error
type T20 = InstanceType<typeof C>; // C
type T21 = InstanceType<any>; // any
type T22 = InstanceType<never>; // any
type T23 = InstanceType<string>; // Error
type T24 = InstanceType<Function>; // Error