<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          這些高階ts內置泛型幫助類型,你用過幾個

          共 15462字,需瀏覽 31分鐘

           ·

          2021-07-29 13:08

          前言

          • 本文將簡要介紹一些工具泛型使用及其實現, 這些泛型接口定義大多數是語法糖(簡寫), 你可以在 typescript 包中的 lib.es5.d.ts 中找到它的定義, 我們項目的版本  "typescript": "^3.9.7",

          關鍵字

          在了解這這些內置幫助類型之前,我們先聊一聊一些關鍵字,有助于了解,因為這些關鍵字和js中的意識還是有出入的,我當時就一臉懵逼

          extends

          • 可以用來繼承一個class,interface,還可以用來判斷有條件類型(很多時候在ts看到extends,并不是繼承的意識)
          • 示例:
          extends U ? X : Y;
          • 上面的類型意思是,若 T 能夠賦值給 U,那么類型是 X,否則為 Y。原理是令 T' 和 U' 分別為 T 和 U 的實例,并將所有類型參數替換為 any,如果 T' 能賦值給 U',則將有條件的類型解析成 X,否則為Y。上面的官方解釋有點繞,下面舉個栗子:
          type Words = 'a'|'b'|"c";

          type W<T> = T extends Words ? true : false;

          type WA = W<'a'>; // -> true

          type WD = W<'d'>; // -> false
          • a 可以賦值給 Words 類型,所以 WA 為 true,而 d 不能賦值給 Words 類型,所以 WD 為 false。

          infer

          • 表示在extends條件語句中待推斷得類型變量(可結合后面的returnType)
          type Union<T> = T extends Array<infer U> ? U: never
          • 如果泛型參數T滿足約束條件Array那么就返回這個類型變量U
          • 有點懵逼再來一個
          type ParamType<T> = T extends (param: infer P) => any ? P: T;
          // 解析如果T能賦值給(param: infer P) => any 類型,就返回P,否則就返回T

          interface IDog {
              name: string;
              age:number;
          }

          type Func = (dog:IDog) => void;

          type Param = ParamType<Func>; // IDog
          type TypeString = ParamType<string// string

          keyof

          • keyof 可以用來取得一個對象接口的所有 key 值:
          • 示例:
          interface IDog {
              name: string;
              age: number;
              sex?: string;
          }

          type K1 = keyof Person; // "name" | "age" | "sex"
          type K2 = keyof Person[];  // "length" | "push" | "pop"  ...
          type K3 = keyof { [x: string]: Person };  // string | number

          typeof

          • 在 JS 中 typeof 可以判斷數據類型,在 TS 中,它還有一個作用,就是獲取一個變量的聲明類型,如果不存在,則獲取該類型的推論類型。
          • 示例:
          interface IDog {
            name: string;
            age: number;
            sex?: string;
          }

          const jack: IDog = { name: 'jack', age: 100 };
          type Jack = typeof jack; // -> IDog

          function foo(x: number): Array<number{
            return [x];
          }

          type F = typeof foo; // -> (x: number) => number[]
          - Jack 這個類型別名實際上就是 jack 的類型 Person,而 F 的類型就是 TS 自己推導出來的 foo 的類型 (x: number) => number[]。

          內置幫助類型

          Partial

          /**
           * Make all properties in T optional
           * 讓T中的所有屬性都是可選的
           */

          type Partial<T> = {
              [P in keyof T]?: T[P];
          };
          • 在某些情況下,我們希望類型中的所有屬性都不是必需的,只有在某些條件下才存在,我們就可以使用Partial來將已聲明的類型中的所有屬性標識為可選的。
          • 示例:
          interface Dog {
           age: number;
           name: string;
           price: number;
          }
           
          type PartialDog = Partial<Dog>;
          // 等價于
          type PartialDog = {
           age?: number;
           name?: string;
           price?: number;
          }
           
          let dog: PartialDog = {
           age: 2,
           name: 'xiaobai'
          };
          • 在上述示例中由于我們使用Partial將所有屬性標識為可選的,因此最終dog對象中雖然只包含age和name屬性,但是編譯器依舊沒有報錯,當我們不能明確地確定對象中包含哪些屬性時,我們就可以通過Partial來聲明。

          Partial

          /**
           * Make all properties in T required
           * 使T中的所有屬性都是必需的
           */

          type Required<T> = {
              [P in keyof T]-?: T[P];
          };
          • Required 的作用剛好跟 Partial 相反,Partial 是將所有屬性改成可選項,Required 則是將所有類型改成必選項:
          • 其中 -? 是代表移除 ? 這個 modifier 的標識。
          • 與之對應的還有個 +? , 這個含義自然與 -? 之前相反, 它是用來把屬性變成可選項的,+ 可省略,見 Partial。
          • 示例:
          interface Dog {
           age: number;
           name: string;
           price: number;
          }
           
          type RequiredDog = Required<Dog>;
          // 等價于
          type RequiredDog = {
           age: number;
           name: string;
           price: number;
          }
           
          let dog: RequiredDog = {
           age?: 2,
           name?: 'xiaobai'
          };

          Readonly

          /**
           * Make all properties in T readonly
           * 將所有屬性設置為只讀
           */

          type Readonly<T> = {
              readonly [P in keyof T]: T[P];
          };
          • 給子屬性添加 readonly 的標識,如果將上面的 readonly 改成 -readonly, 就是移除子屬性的 readonly 標識。
          • 示例:
          interface IDog{
              name: string;
              age: number;
          }
          type TDog = Readonly<IDog>;
          class TestDog {
              run() {
                  let dog: IDog = {
                      name: 'dd',
                      age: 1
                  };
                  person.name = 'cc';
                  let dog1: TDog = {
                      name: 'read',
                      age: 1
                  };
                  // person2.age = 3; 報錯,不能賦值
              }
          }

          Pick

          /**
           * From T, pick a set of properties whose keys are in the union K
           * 從T中,選擇一組鍵在并集K中的屬性
           */

          type Pick<T, K extends keyof T> = {
              [P in K]: T[P];
          };
          • 從源碼可以看到 K 必須是 T 的 key,然后用 in 進行遍歷, 將值賦給 P, 最后 T[P] 取得相應屬性的值。
          • 示例:
          interface IDog {
           name: string;
           age: number;
           height: number;
           weight: number;
          }
           
          type PickDog = Pick<IDog, "name" | "age" | "height">;
          // 等價于
          type PickDog = {
           name: string;
           age: number;
           height: number;
          };
           
          let dog: PickDog = {
           name: 'wangcai',
           age: 3,
           height: 70
          };
          • 在上述示例中,由于我們只關心IDog對象中的name,age和height是否存在,因此我們就可以使用Pick從IDog接口中揀選出我們關心的屬性而忽略其他屬性的編譯檢查。

          Record

          /**
           * Construct a type with a set of properties K of type T
           * 構造一個具有一組屬性K(類型T)的類型
           */

           
          type Record<K extends keyof any, T> = {
              [P in K]: T;
          };
          • 可以根據 K 中的所有可能值來設置 key,以及 value 的類型
          • 示例:
          let dog = Record<stringstring | number | undefined>; // -> string | number | undefined

          該類型可以將 K 中所有的屬性的值轉化為 T 類型,并將返回的新類型返回給dog,K可以是聯合類型、對象、枚舉…

          • 示例:
          type petsGroup = 'dog' | 'cat';
          interface IPetInfo {
              name:string,
              age:number,
          }

          type IPets = Record<petsGroup, IPetInfo>;

          const animalsInfo:IPets = {
              dog:{
                  name:'wangcai',
                  age:2
              },
              cat:{
                  name:'xiaobai',
                  age:3
              },
          }

          Exclude

          /**
           * Exclude from T those types that are assignable to U
           * 從T中排除那些可分配給U的類型
           */

          type Exclude<T, U> = T extends U ? never : T;
          • 與Pick相反,Pick用于揀選出我們需要關心的屬性,而Exclude用于排除掉我們不需要關心的屬性
          • 示例:
          interface IDog {
           name: string;
           age: number;
           height: number;
           weight: number;
           sex: string;
          }
           
          type keys = keyof IDog; // -> "name" | "age" | "height" | "weight" | "sex"
           
          type ExcludeDog = Exclude<keys, "name" | "age">;
          // 等價于
          type ExcludeDog = "height" | "weight" | "sex";
          • 在上述示例中我們通過在ExcludeDog中傳入我們只關心的height、weight、sex屬性,Exclude會幫助我們將不需要的屬性進行剔除。留下的屬性id,name和gender即為我們需要關心的屬性。
          • 示例:
          type T = Exclude<1 | 21 | 3// -> 2
          • 很輕松地得出結果 2根據代碼和示例我們可以推斷出 Exclude 的作用是從 T 中找出 U 中沒有的元素, 換種更加貼近語義的說法其實就是從T 中排除 U
          • 一般來說,Exclude很少單獨使用,可以與其他類型配合實現更復雜更有用的功能。

          Extract

          /**
           * Extract from T those types that are assignable to U
           * 從T中提取可分配給U的類型
           */

          type Extract<T, U> = T extends U ? T : never;
          • Extract 的作用是提取出 T 包含在 U 中的元素,換種更加貼近語義的說法就是從 T 中提取出 U
          • 以上語句的意思就是 如果 T 能賦值給 U 類型的話,那么就會返回 T 類型,否則返回 never,最終結果是將 T 和 U 中共有的屬性提取出來
          • 示例:
          type test = Extract<'a' | 'b' | 'c' | 'd''a' | 'c' | 'f'|'g'>;  // -> 'a' | 'c'
          • 可以看到 T 是 'a' | 'b' | 'c' | 'd' ,然后 U 是 'a' | 'c' | 'f'|'g' ,返回的新類型就可以將 T 和 U 中共有的屬性提取出來,也就是 'a' | 'c' 了。

          Omit

          /**
           * Construct a type with the properties of T except for those in type K.
           * 構造一個除類型K之外的T屬性的類型
           */

           type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
          • 在上一個用法中,我們使用Exclude來排除掉其他不需要的屬性,但是在上述示例中的寫法耦合度較高,當有其他類型也需要這樣處理時,就必須再實現一遍相同的邏輯,使用Omit可以避免這些問題,老版本ts未內置,TypeScript 3.5已經內置:
          • 示例:
          interface IDog {
          name: string;
          age: number;
          height: number;
          weight: number;
          sex: string;
          }

          // 表示忽略掉User接口中的name和age屬性
          type OmitDog = Omit<IDog, "name" | "age">;
          // 等價于
          type OmitDog = {
          height: number;
          weight: number;
          sex: string;
          };

          let dog: OmitDog = {
          height: 1,
          weight: 'wangcai',
          sex: 'boy'
          };
          • 在上述示例中,我們需要忽略掉IDog接口中的name和age屬性,則只需要將接口名和屬性傳入Omit即可,對于其他類型也是如此,大大提高了類型的可擴展能力,方便復用

          NonNullable

          /**
           * Exclude null and undefined from T
           * 從T中排除null和undefined
           */

          type NonNullable<T> = T extends null | undefined ? never : T;
          • 這個類型可以用來過濾類型中的 null 及 undefined 類型。
          • 示例:
          type test = string | number | null;
          type test1 = NonNullable<test>; // -> string | number;

          Parameters

          /**
           * Obtain the parameters of a function type in a tuple
           * 在元組中獲取構造函數類型的參數
           */

          type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
          • 該類型可以獲得函數的參數類型組成的元組類型。
          • 示例:
          function foo(x: number): Array<number{
            return [x];
          }

          type P = Parameters<typeof foo>; // -> [number]
          • 此時 P 的真實類型就是 foo 的參數組成的元組類型 [number]。

          ConstructorParameters

          /**
           * Obtain the parameters of a constructor function type in a tuple
           * 在元組中獲取構造函數類型的參數
           */

          type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
          • 該類型的作用是獲得類的參數類型組成的元組類型
          • 示例:
          class Person {
            private firstName: string;
            private lastName: string;
            
            constructor(firstName: string, lastName: string) {
                this.firstName = firstName;
                this.lastName = lastName;
            }
          }

          type P = ConstructorParameters<typeof Person>; // -> [string, string]
          • 此時 P 就是 Person 中 constructor 的參數 firstName 和 lastName 的類型所組成的元組類型 [string, string]。

          ReturnType

          /**
           * Obtain the return type of a function type
           * 獲取函數類型的返回類型
           */

          type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
          • 該類型的作用是獲取函數的返回類型。
          • 其實這里的 infer R 就是聲明一個變量來承載傳入函數簽名的返回值類型, 簡單說就是用它取到函數返回值的類型方便之后使用
          • 實際使用的話,就可以通過 ReturnType 拿到函數的返回類型
          • 示例:
          function foo(x: number): Array<number{
            return [x];
          }

          type fn = ReturnType<typeof foo>; // -> number[]

          InstanceType

          /**
           * Obtain the return type of a constructor function type
           * 獲取構造函數類型的返回類型
           */


          type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
          • 該類型的作用是獲取構造函數類型的實例類型。
          class ConstructorType {
              x = 0;
              y = 0;
          }
          type test1 = InstanceType<typeof ConstructorType>;  // ConstructorType

          type test1 = InstanceType<any>;  // any

          ThisType

          /**
           * Marker for contextual 'this' type
           * 上下文“this”類型的標記
           */

          interface ThisType<T> { }
          • 這個類型是用于指定上下文對象類型的。
          • 這類型怎么用呢,舉個例子:
          interface Cat {
              name: string;
              age: number;
          }
          const obj: ThisType<Person> = {
            mimi() {
              this.name // string
            }
          }
          • 這樣的話,就可以指定 obj 里的所有方法里的上下文對象改成 Person 這個類型了。
          // 沒有ThisType情況下
          const dog = {
              wang() {
                   console.log(this.age); // error,在dog中只有wang一個函數,不存在a
              }
          }
          // 使用ThisType
          const dog: { wang: any } & ThisType<{ age: number }> = {
              wang() {
                   console.log(this.wang) // error,因為沒有在ThisType中定義
                   console.log(this.age); // ok
              }
          }
          dog.wang // ok 正常調用
          dog.age // error,在外面的話,就跟ThisType沒有關系了,這里就是沒有定義age了
          • 從上面的代碼中可以看到,ThisType的作用是:提示其下所定義的函數,在函數body中,其調用者的類型是什么。

          參考

          • https://segmentfault.com/a/1190000018514540?utm_source=tag-newest
          • 深入理解typescript

          • 歡迎加我微信(lisawhy0706),拉你進技術群,長期交流學習

          • 歡迎關注「前端要努力」,認真學前端,免費獲取大量面試資料


          瀏覽 59
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男女WWWWWWWWW | 天天色成人网站 | 久久久精品三级片 | 中文字幕 国产精品 | 久热这里有精品 |