高級類型

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中剔除nullundefined
  • 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

 

 

 

 

 

 

 

 

 

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