<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      TypeScript(十)泛型進階

      目錄

      前言

      泛型約束

      聯合類型+泛型約束

      交叉類型+泛型約束

      泛型約束泛型

      遞歸類型別名

      條件類型

      分發條件類型

      類型過濾

      類型推導

      infer關鍵字

      回到類型推導

      映射&索引類型

      索引訪問類型

      映射類型

      必選屬性

      可變屬性

      結語

      相關文章


      前言

      本文收錄于TypeScript知識總結系列文章,歡迎指正! 

      上篇文章我們領略了泛型的靈活及強大;了解了泛型的基本使用以及常見用法。本文將針對泛型的其他用法做一些進階拓展,其中有許多知識點可以放在前面的文章介紹,但是與泛型放在一起可能更好理解,那么話不多說,直接開始

      泛型約束

      我們可以通過泛型約束來對泛型參數類型進行限制,確保它符合特定的類型要求,泛型約束的寫法是在關鍵字extends后追加類型來實現,約束的類型可以是任意類型下面是一個簡單的例子

      type Animal<T extends hobbyList> = {
          name: string
          hobby: T
      }
      type hobbyList = {
          length: number
          push: (...args: any[]) => number
      }
      const animal: Animal<hobbyList> = {
          name: "阿黃",
          hobby: ['ball', 'flying-disc']
      }
      animal.hobby.push("run")
      
      console.log(animal.hobby); //  [ 'ball', 'flying-disc', 'run' ]
      

      可以看到,上面的函數中我們限制了animal傳入的泛型T有且僅包含length屬性及push方法,此時我們對animal中的hobby修改類型或者調用其他方法時就會報錯

      animal.hobby.concat(["run"]) // 類型“hobbyList”上不存在屬性“concat”
      animal.hobby = {} // 類型“{}”缺少類型“hobbyList”中的以下屬性: length, push

      聯合類型+泛型約束

      之前的文章中我們接觸到了聯合類型,在泛型的約束中同樣可以應用該場景中,表示泛型類型參數必須是多個類型中的一種,如

      type Animal<T extends string | string[]> = {
          name: string
          hobby: T
      }
      const animal: Animal<string> = {
          name: "阿黃",
          hobby: "ball"
      }
      const animal2: Animal<string[]> = {
          name: "阿黃",
          hobby: ['ball', 'flying-disc']
      }
      

      交叉類型+泛型約束

      除了上面的聯合類型外,我們還可以使用交叉類型來限制泛型同時滿足多個類型特征,比如

      type Animal<T extends arrayLength & arrayPush> = {
          name: string
          hobby: T
      }
      type arrayLength = {
          length: number
      }
      type arrayPush = {
          push: (...args: any[]) => number
      }
      type MyArray<T> = arrayLength & arrayPush & {
          forEach: (cb: (item: T, i: number, arr: MyArray<T>) => void) => void
      }
      const animal: Animal<MyArray<string>> = {
          name: "阿黃",
          hobby: ['ball', 'flying-disc']
      }
      animal.hobby.push("run")
      console.log(animal.hobby.length); // 3

      上面的代碼中type MyArray同時拓展了arrayPush,arrayLength兩個接口,并在其基礎上新增了forEach方法,此時是可以正確賦值給Animal的泛型的

      泛型約束泛型

      所謂泛型約束泛型,就是在泛型類型參數中使用其他泛型類型參數來約束它的類型,有點套娃的意思,比如我們對上面的代碼做一些修改:在Animal中增加一個getHobby的屬性,這個屬性類型是U(即arrayLength),而T是被約束于U的,所以T依舊可以取MyArray<string>,簡單的說就是T類型是U類型的子類,子類的屬性是只能多不能少的

      type Animal<T extends U, U> = {
          name: string
          hobby: T
          getHobby: U
      }
      type arrayLength = {
          length: number
      }
      type arrayPush = {
          push: (...args: any[]) => number
      }
      type MyArray<T> = arrayLength & arrayPush & {
          forEach: (cb: (item: T, i: number, arr: MyArray<T>) => void) => void
      }
      const animal: Animal<MyArray<string>, arrayLength> = {
          name: "阿黃",
          hobby: ['ball', 'flying-disc'],
          getHobby: {
              length: 2
          }
      }
      animal.hobby.push("run")
      console.log(animal.getHobby.length);// 2
      console.log(animal.hobby.length); // 3

      遞歸類型別名

      當我們在寫對象的接口與類型別名時可能會遇到樹形結構或者嵌套對象的情況,如原型鏈,菜單的子項,此時我們需要一種遞歸結構,允許我們在類型中調用自己,如

      type MenuItem = {
          label: string
          key: string
          url: string
      }
      type Menu<T> = {
          value: T
          children?: Menu<T>[]
      }
      
      const menu: Menu<MenuItem> = {
          value: {
              label: '菜單1',
              key: 'menu1',
              url: '/menu1'
          },
          children: [
              {
                  value: {
                      label: '子菜單1',
                      key: 'child1',
                      url: '/child1'
                  },
                  children: [
      
                  ]
              }, {
                  value: {
                      label: '子菜單2',
                      key: 'child2',
                      url: '/child2'
                  },
                  children: [
      
                  ]
              },
          ]
      }
      

      上面的代碼中我們使用遞歸類型別名實現了一個簡單的二級菜單

      條件類型

      在JS中,我們都使用過三元運算符:a ? b : c。在TS中也有這種寫法,我們稱其為條件類型,它可以根據類型參數的屬性或其他類型信息選擇類型的一部分,比如

      type ReturnObject<T> = T extends { [key: string]: any } ? T : null
      type isNotObj = ReturnObject<false>
      type isObj = ReturnObject<{ name: "張三" }>

      上面的代碼我們實現了對象類型的約束,如果傳入的類型是對象類型則返回該類型,否則返回null 

      分發條件類型

      TS中的分發條件類型可以將復雜的類型轉換分解成更小的類型,并最終將它們組合在一起

      比如我們使用條件類型實現一個簡單的類型檢查器,如果是數字,字符或布爾類型就獲取各自的字符串,否則返回other字符串

      type IGetType<T> = T extends string ? 'str'
          : T extends number ? 'num'
          : T extends boolean ? 'bool'
          : 'other'
      type INum = IGetType<number>// num
      type IBool = IGetType<boolean>// bool
      type IStr = IGetType<string>// str
      type IOther = IGetType<unknown>// other

      類型過濾

      顧名思義類型過濾就是在一個集合中過濾出符合條件的類型

      基于上面的概念,我們可以實現一個include的類型檢查,下面的函數中我們實現了一個類型過濾,只允許字符、數字、布爾類型通過

      type MyInclude<T, U> = U extends T ? U : never;
      type whiteList = string | number | boolean // 允許的類型 
      type getString = MyInclude<whiteList, string> // string
      type getArray = MyInclude<whiteList, string[]> // never

      類型推導

      infer關鍵字

      在了解類型推導前,我們要先熟悉一下infer關鍵字,在2.8版本的TS中出現了這樣一個MR

      type ReturnType<T> = T extends (...args: any[]) => infer R ? R : T;

      上述代碼中的infer R表示什么?我們先看看使用場景

      type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : T;
      type IGetStr = MyReturnType<() => string> // string
      type IGetNum = MyReturnType<() => number> // number
      type IStr = MyReturnType<string>// string

      明白了嗎?上面代碼中的infer將函數的返回值提取成R,當我們傳入一個函數類型時,MyReturnType類型就會返回該函數的返回值,否則就返回原類型。

      思考以下類型的實現

      type MyArrayItem<T> = T extends (infer Item)[] ? Item : T;
      type IStr = MyArrayItem<string>// string
      type INumArr = MyArrayItem<number[]>// number

      上面代碼中我們實現了提取數組的子項類型的功能

      回到類型推導

      實際上類型推導就是根據函數參數的類型,推導出函數返回值的類型,我們借助上面的MyReturnType對函數的返回值類型進行推導

      type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : T;
      const concatStr = (str1: string, str2: string) => {
          return str1 + str2
      }
      const addNum = (num1: number, num2: number) => {
          return num1 + num2
      }
      type concatStrReturn = MyReturnType<typeof concatStr> // string
      type concatNumReturn = MyReturnType<typeof addNum> // number

      映射&索引類型

      之前的文章中,我們介紹了keyof和in關鍵字,并且使用映射與索引類型對對象類型進行了復制,并將屬性值設置成了string

      type IAnimal = {
          name: string
          age: number
          hobby: string[]
      }
      type IDog = {
          [key in keyof IAnimal]: string
      }

      索引訪問類型

      在JavaScript中,我們使用對象的索引取對象屬性:Object.key或者Object['key'],取對象中的key屬性,而在TypeScript中我們可以通過Object[key]來取對象類型的key屬性

      于是,我們可以寫一個獲取對象屬性的類型

      interface IAnimal {
          name: string
          age: number
          hobby: string[]
      }
      type GetItem<T, K extends keyof T> = T[K]
      type AnimalName = GetItem<IAnimal, 'name'>// string
      type AnimalAge = GetItem<IAnimal, 'age'>// number

      映射類型

      基于上述代碼,我們可以進一步拓展,使用IAnimal[key]表示IAnimal的每一個屬性,這個key可以理解成是一個對象屬性名的集合(聯合類型),即 name | age | hobby

      type IDog = {
          [key in keyof IAnimal]: IAnimal[key]
      }

      上述代碼表示的就是IAnimal的每一項

      通過這種寫法,我們可以將IAnimal提取成泛型,寫一個通用的類型別名函數,達到遍歷對象每一個屬性并設置成只讀的目的

      type ReadonlyObject<T> = { readonly [key in keyof T]: T[key] };

      我們來試用一下

      type IAnimalReadonly = ReadonlyObject<IAnimal>
      /*
      等同于
      type IAnimalReadonly = {
          readonly name: string;
          readonly age: number;
          readonly hobby: string[];
      }
      */

      必選屬性

      在TS的映射類型中,有許多可選屬性,我們要如何批量改成必選屬性呢?

      這個時候我們可以給屬性名添加 '-?' 符號達到該目的,如

      type IAnimal = {
          name?: string
          age?: number
          hobby?: string[]
      }
      type IDog = {
          [key in keyof IAnimal]-?: string
      }

      此時的IDog的每一項就都是必選屬性了

      可變屬性

      與只讀屬性readonly對應的是可變屬性,和上述必選屬性類似通過在屬性名前加 -readonly 來實現此效果

      type IAnimal = {
         readonly name: string
         readonly age: number
         readonly hobby: string[]
      }
      type Mutable<T> = {
         -readonly [key in keyof T]: string
      }
      type IAni = Mutable<IAnimal>

      結語

      以上就是文章所有內容,本文針對泛型的進階使用,主要講述了泛型的約束、遞歸類型別名、條件類型、映射和索引類型。以及它們的詳細用法

      感謝你看到最后,如果文章對你有幫助還請支持一下!

      相關文章

      索引簽名 | 深入理解 TypeScript

      泛型 | 深入理解 TypeScript

      infer | 深入理解 TypeScript

      posted @ 2023-04-03 13:18  阿宇的編程之旅  閱讀(41)  評論(0)    收藏  舉報  來源
      主站蜘蛛池模板: 越西县| 久久精品国产99久久无毒不卡| 国产激情一区二区三区不卡| 久久久久综合中文字幕| 精品人妻蜜臀一区二区三区| 国产色视频一区二区三区qq号| 色综合天天色综合久久网| 古田县| 亚洲av永久无码精品漫画| 狠狠躁夜夜躁无码中文字幕| 国产精品一区二区三区四区| 深夜福利啪啪片| 日韩精品亚洲不卡一区二区| 成人亚洲性情网站www在线观看| 1024你懂的国产精品| 国产性色的免费视频网站| 亚洲va在线∨a天堂va欧美va| 久久久久人妻一区精品| 2019国产精品青青草原| 精品人伦一区二区三区蜜桃免费| 亚在线观看免费视频入口| 亚欧成人精品一区二区乱| 久久99久久99精品免视看国产成人| 天天躁夜夜躁狠狠喷水| 九九热在线免费视频精品| 激情综合一区二区三区| 人妻精品动漫h无码| 亚洲乱码av中文一区二区| 四虎国产精品成人免费久久 | 久久国产精品伊人青青草| 我国产码在线观看av哈哈哈网站| 国产微拍一区二区三区四区| 国产免费视频一区二区| 91福利国产午夜亚洲精品| 无遮无挡爽爽免费视频| 国产精品v欧美精品∨日韩| 亚洲va久久久噜噜噜久久狠狠| 佛冈县| 正在播放肥臀熟妇在线视频| 亚洲成人av在线系列| 四虎库影成人在线播放|