<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>

          TypeScript 內(nèi)置工具詳談

          共 11913字,需瀏覽 24分鐘

           ·

          2021-08-13 16:14

          點(diǎn)擊上方 前端瓶子君,關(guān)注公眾號(hào)

          回復(fù)算法,加入前端編程面試算法每日一題群

          前言

          TypeScript 提供了幾種實(shí)用程序類(lèi)型來(lái)助力常見(jiàn)的類(lèi)型轉(zhuǎn)換。這些實(shí)用程序是全局可用的。

          也就是說(shuō)全局聲明了一些Type, 調(diào)用Type就可以方便地進(jìn)行一些類(lèi)型轉(zhuǎn)換或者創(chuàng)建新的類(lèi)型。
          不會(huì)這些函數(shù)一樣能寫(xiě)TypeScript你不會(huì)真的就不看下文了吧???, 但是掌握后能讓你寫(xiě)TypeScript事半功倍。且掌握這些內(nèi)置Type是十分必要的。

          本文章主要對(duì)一些比較少用或者難理解的類(lèi)型做了比較詳細(xì)的說(shuō)明。比如 ThisType<T>

          1、Partial 將一個(gè)類(lèi)型的屬性全部變?yōu)榭蛇x

          定義

          type Partial<T> = {
              [P in keyof T]?: T[P];
          };
          復(fù)制代碼

          從上面的代碼中可以看出來(lái)該Type使用時(shí)需要傳入一個(gè)泛型T。內(nèi)部遍歷T的所有屬性然后創(chuàng)建一個(gè)新的 Type,新的Type的所有屬性使用 ? 標(biāo)識(shí),使之為可選。

          keyof會(huì)遍歷一個(gè)Interface的所有屬性名稱(chēng)(key), 生成一個(gè)聯(lián)合類(lèi)型 "name" | "age" ...,然后可以得到下面代碼

          P in "name" | "age" 這就很明白能看出來(lái)了,表明了P為右側(cè)類(lèi)型

          使用案例

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

          // 這里會(huì)將 UserInfo 所有的屬性變?yōu)榭蛇x
          const foo:Partial<UserInfo> = {
              name:"張三" 
          }
          復(fù)制代碼

          2、Required 將一個(gè)類(lèi)型的屬性全部變?yōu)楸剡x

          定義

          type Required<T> = {
              [P in keyof T]-?: T[P];
          };
          復(fù)制代碼

          TypePartial剛好是相反的。從上面的代碼中可以看出來(lái)該Type實(shí)用時(shí)需要傳入一個(gè)泛型T。內(nèi)部使用-?T的每個(gè)屬性去除可選標(biāo)識(shí)使之變成為必填。

          使用案例

          interface UserInfo {
              name?:string;
              age?:number;
          }

          // 這里會(huì)將 UserInfo 所有可選的屬性變?yōu)楸剡x
          const foo:Required<UserInfo> = {
              name:"張三",
              age:18
          }
          復(fù)制代碼

          3、Readonly 將一個(gè)類(lèi)型的屬性全部變?yōu)橹蛔x狀態(tài)

          定義

          type Readonly<T> = {
              readonly [P in keyof T]: T[P];
          };
          復(fù)制代碼

          從上面的代碼中可以看出來(lái)該Type實(shí)用時(shí)需要傳入一個(gè)泛型T。內(nèi)部使用readonlyT的每個(gè)屬性去除可選標(biāo)識(shí)使之變成為只讀。

          使用案例

          interface UserInfo {
              name?:string;
              age?:number;
          }
           
          const foo:Readonly<UserInfo> = {
              name:"張三",
              age:18
          }
          foo.name = '李四';// error: 無(wú)法分配到 "name" ,因?yàn)樗侵蛔x屬性
          復(fù)制代碼

          4、Record 構(gòu)造一個(gè)字面量對(duì)象 Type

          定義

          type Record<K extends keyof any, T> = {
              [P in K]: T;
          };
          復(fù)制代碼

          Record 用于方便地構(gòu)造一個(gè)字面量對(duì)象。其作用和 { [propName:string]:any } 有些許類(lèi)似。

          Record 只需要傳入兩個(gè) Type 即可創(chuàng)建一個(gè)新的 Type,相比于 { [propName:string]:any } 能方便一些。當(dāng)然除了方便外功能也比它強(qiáng)大,因?yàn)?code style="font-size: 14px;word-wrap: break-word;border-radius: 4px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #9b6e23;background-color: #fff5e3;padding: 3px;margin: 3px;">Record第一個(gè)參數(shù)可接收一組key,這樣就可以做到定義出一個(gè)完整的 Type 了。

          使用案例

          // 這是通過(guò) interface 定義出來(lái)的。
          interface UserInfo {
              name:string;
              age:number;
          }

          // 我們用 Record 來(lái)實(shí)現(xiàn)一遍 UserInfo 。
          // 注意:后面一個(gè)形參和 UserInfo 的是不一樣的,因?yàn)?Record 第二個(gè)參數(shù)只能接受一個(gè)類(lèi)型。所以這里要么用 any,要么用這種聯(lián)合類(lèi)型。
          type UserInfoT = Record<"name" | "age", string | number>

          // 結(jié)果
          // type UserInfoT = {
          //     name:string | number;
          //     age:string | number;
          // }
          復(fù)制代碼

          5、Pick 從一個(gè) Type 中選取一些屬性來(lái)構(gòu)造一個(gè)新的對(duì)象 Type

          定義

          type Pick<T, K extends keyof T> = {
              [P in K]: T[P];
          };
          復(fù)制代碼

          Pick 也用于方便地構(gòu)造一個(gè)字面量對(duì)象。其作用和 Record 有些許類(lèi)似。

          使用案例

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

          // 這時(shí)候我們只需要 UserInfo 的 name 屬性。
          type UserInfoT = Pick<UserInfo, "name">
          復(fù)制代碼

          6、Omit 從一個(gè)對(duì)象類(lèi)型中刪除一些屬性來(lái)構(gòu)造一個(gè)新的對(duì)象 Type

          定義

          type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
          復(fù)制代碼

          日常使用中Omit 是一個(gè)使用頻率可能比較高的。和 Pick 剛剛相反,用于排除不需要的屬性。

          使用案例

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

          // 這時(shí)候我們不需要 UserInfo 的 name 屬性。
          type UserInfoT = Omit<UserInfo, "name">
          復(fù)制代碼

          7、Exclude 排除一個(gè)聯(lián)合類(lèi)型中的某一些類(lèi)型來(lái)構(gòu)造一個(gè)新 Type

          定義

          type Exclude<T, U> = T extends U ? never : T;
          復(fù)制代碼

          上面說(shuō)的 OmitPick 都是對(duì)一個(gè)字面量對(duì)象 Type 的操作。如果要對(duì)一個(gè)聯(lián)合類(lèi)型操作的話(huà)需要用到 ExcludeExtract

          使用案例

          // 排除掉 "name"
          type UserInfoT = Exclude<"name" | "age""name">;

          // 等價(jià)于
          type UserInfoA = "age";
          復(fù)制代碼

          8、Extract 提取出一個(gè)聯(lián)合類(lèi)型中的某一些類(lèi)型來(lái)構(gòu)造一個(gè)新 Type

          定義

          type Extract<T, U> = T extends U ? T : never;
          復(fù)制代碼

          Exclude 恰好相反。

          使用案例

          // 從 T1 中 提取出 T2
          type T1 = "name" | "age" | "hob";
          type T2 = "name" | "age";
          type UserInfoT = Extract<T1, T2>;

          // 等價(jià)于
          type UserInfoA = "name" | "age";
          復(fù)制代碼

          既然是提出哪為啥不直接用定義好的 T2?

          因?yàn)檫@樣可以保證 UserInfoT 的類(lèi)型一定是在 T1 中存在的;

          9、NonNullable 從類(lèi)型中排除 null 和 undefined 來(lái)構(gòu)造一個(gè)新的 Type

          定義

          type NonNullable<T> = T extends null | undefined ? never : T;
          復(fù)制代碼

          使用案例

          // 從 UserInfoK 中 排除掉 null | undefined 
          type UserInfoK = NonNullable<"name" | "hob" | undefined>;

          // 等價(jià)于
          type UserInfoKA = "name" | "hob";
          復(fù)制代碼

          10、Parameters 從 [函數(shù) Type] 的形參構(gòu)造一個(gè)數(shù)組 Type

          定義

          type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
          復(fù)制代碼

          infer標(biāo)識(shí)一個(gè)待推導(dǎo)類(lèi)型,上面定義的意思是:如果 T 為函數(shù)類(lèi)型,那就返回函數(shù)的形參。

          ps: infer和變量似的,先定義一個(gè) infer P 然后 Ts 就會(huì)自動(dòng)推導(dǎo)函數(shù)的形參或者返回值、或者數(shù)組元素等,然后開(kāi)發(fā)者在合適的位置使用定義好的infer P即可。

          一個(gè)簡(jiǎn)單的infer案例。

          加入有這樣一個(gè)需求:需要將數(shù)組類(lèi)型的 Type 變?yōu)槁?lián)合類(lèi)型。其他類(lèi)型的則不變。這樣我們就可以寫(xiě)一個(gè)這樣的 Type

          type ArrayToUnion<T> = T extends Array<infer Item> ? Item : T;

          const a:ArrayToUnion<[string, number]> = "111"; // a: string | number
          const b:ArrayToUnion<string | number> = "111"; // a: string | number
          復(fù)制代碼

          從這個(gè)案列的a變量可以看出作用,a變量的類(lèi)型定義為ArrayToUnion<[string, number]>,這里傳入的是個(gè)數(shù)組[string, number]ArrayToUnion處理為了string | number

          使用案例

          // 定義一個(gè)函數(shù)
          function getUserInfo(id:string, group:string){}

          // 獲取到函數(shù)需要的形參 Type[]
          type GetUserInfoArg = Parameters<typeof getUserInfo>;
             
          const arg:GetUserInfoArg = [ "001""002" ];

          getUserInfo(...arg);
          復(fù)制代碼

          ps: 上面代碼中的typeof是 ts 提供的操作符不是 js 中的那個(gè)typeof,只能用到 ts 的類(lèi)型定義中, 所以使用typeof getUserInfo才能指向函數(shù)Type

          11、ConstructorParameters 從定義的[構(gòu)造函數(shù)]的形參構(gòu)造數(shù)組 Type

          定義

          type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;
          復(fù)制代碼

          實(shí)現(xiàn)原理完全和 Parameters 一樣,只不過(guò)這個(gè)方法接受的事一個(gè)類(lèi)。

          使用案例

          class User{
              constructor(id:string, group:string){}
          }

          type NewUserArg =  ConstructorParameters<typeof User>;

          const arg:NewUserArg = [ "001""002"];

          new User(...arg);
          復(fù)制代碼

          12、ReturnType 用函數(shù) Type 的返回值定義一個(gè)新的 Type

          定義

          type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
          復(fù)制代碼

          使用 infer 實(shí)現(xiàn)。比 Parameters 更簡(jiǎn)單,可以去看上面的 Parameters 就能明白這段代碼意思。

          使用案例

          // 定義一個(gè)函數(shù) Type
          type GetUserInfo = ()=>string;

          const rt:ReturnType<GetUserInfo> = 'xxx';
          復(fù)制代碼

          13、InstanceType 從一個(gè)構(gòu)造函數(shù)的實(shí)例定義一個(gè)新的 Type

          定義

          type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
          復(fù)制代碼

          使用 infer 實(shí)現(xiàn)。和ReturnType實(shí)現(xiàn)原理完全一樣。

          使用案例

          // 定義一個(gè)函數(shù) Type
          type GetUserInfo = ()=>string;

          const rt:ReturnType<GetUserInfo> = 'xxx';
          復(fù)制代碼

          14、ThisParameterType 提取函數(shù) Type 的 this 參數(shù)生成一個(gè)新的 Type

          定義

          type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;
          復(fù)制代碼

          從上面定義看出該 Type 對(duì)函數(shù)的第一個(gè)形參 this 做了infer推導(dǎo)。然后返回了推導(dǎo)出來(lái)的this。不清楚infer的話(huà),往上翻,去仔細(xì)看看Parameters一節(jié)的說(shuō)明。

          使用案例

          // 定義一個(gè)函數(shù),并且定義函數(shù) this 類(lèi)型。 
          function getUserInfo(this:{ name:string }){}

          const getUserInfoArgThis: ThisParameterType<typeof getUserInfo> = {
              name:"王"
          };
          復(fù)制代碼

          15、OmitThisParameter 忽略函數(shù) Type 的 this 參數(shù),生成一個(gè)新的函數(shù) Type

          定義

          type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;
          復(fù)制代碼

          這個(gè)Type看著略微復(fù)雜。咋們拆一下看就會(huì)簡(jiǎn)單很多。

          首先說(shuō)明一下這個(gè)Type的這些判斷都是干嘛的。

          上面定義意思是:如果傳入的T沒(méi)有this參數(shù)就直接返回T,如果有this參數(shù)就繼續(xù)進(jìn)行判斷,

          第二層判斷為:如果T不是函數(shù)那也會(huì)直接返回T,最后是重新定義了一個(gè)函數(shù)然后返回。其中使用infer定義了我們所需要的形參和返回值。

          這里在座的各位可能會(huì)在(...args: infer A) => infer R ? (...args: A) => R : T這里產(chǎn)生疑惑。

          上面的寫(xiě)法會(huì)直接把this參數(shù)過(guò)濾掉,為了證實(shí)這點(diǎn),我們可以實(shí)現(xiàn)一下:

          type NoThis<T> = T extends (...args: infer A) => infer R ? A : T

          const a:NoThis<typeof getUserInfo>; // a: [id: string]
          復(fù)制代碼

          上面代碼中我們直接返回了推導(dǎo)的A,得到了形參A的類(lèi)型。這里面是不會(huì)包含this的。

          使用案例

          // 定義一個(gè)函數(shù)
          function getUserInfo(this:{ name:string }, id:string){}

          // 去除 getUserInfo 函數(shù) this 參,然后創(chuàng)建出來(lái)了一個(gè)新類(lèi)型
          const aaa: OmitThisParameter<typeof getUserInfo> = (id:string)=>{} 
          復(fù)制代碼

          16、ThisType 給對(duì)象標(biāo)記 this 接口

          這個(gè)類(lèi)型在 lib.d.ts 中定義的就是一個(gè){}空標(biāo)簽,所以用的時(shí)候往往比較困惑。特別是沒(méi)注意看到官網(wǎng)上寫(xiě)的必須開(kāi)啟--noImplicitThis時(shí)才可以用的時(shí)候。就算你看到了,但是你在他們案例中如果不注意的話(huà)還是搞不懂,因?yàn)楣俜桨咐性O(shè)置了這個(gè)編譯規(guī)則 // @noImplicitThis: false

          noImplicitThis 規(guī)則開(kāi)啟后在函數(shù)中的this在不定義的情況下不能使用,相當(dāng)于嚴(yán)格模式,默認(rèn)情況下noImplicitThis的值為false,除非手動(dòng)開(kāi)啟,否則ThisType毫無(wú)作用。

          使用案例

          // 定義一個(gè)函數(shù)
          function getUserInfo(this:{ name:string }, id:string){}

          // 去除 getUserInfo 函數(shù) this 參,然后創(chuàng)建出來(lái)了一個(gè)新函數(shù)類(lèi)型
          const aaa: OmitThisParameter<typeof getUserInfo> = (id:string)=>{} 
          復(fù)制代碼

          17、Uppercase 將字符串中的每個(gè)字符轉(zhuǎn)換為大寫(xiě)

          這是對(duì)字符串的操作,所有對(duì)字符串的操作在 lib.d.ts 中都找不到具體的定義,文檔上說(shuō)是為了提升性能。

          type MyText = "Hello, world" 
          type A = Uppercase<MyText>; // type A = "HELLO, WORLD"
          復(fù)制代碼

          18、Lowercase 將字符串中的每個(gè)字符轉(zhuǎn)換為小寫(xiě)

          type MyText = "Hello, world" 
          type A = Lowercase<MyText>; // type A = "hello, world"
          復(fù)制代碼

          19、Capitalize 將字符串中的第一個(gè)字符轉(zhuǎn)換為大寫(xiě)

          type MyText = "hello, world" 
          type A = Capitalize<MyText>; // type A = "Hello, world"
          復(fù)制代碼

          20、Uncapitalize 將字符串中的第一個(gè)字符轉(zhuǎn)換為小寫(xiě)

          type MyText = "Hello, world" 
          type A = Uncapitalize<MyText>; // type A = "hello, world"
          復(fù)制代碼

          以上就是全部的內(nèi)容啦~

          一款 javascript AST 節(jié)點(diǎn)操作插件推薦:

          qnn-object-ast-handle[1] -使用操作 js 字面量對(duì)象的方式來(lái)操作代碼文件。使 AST 節(jié)點(diǎn)操作變得毫不費(fèi)力。

          關(guān)于本文

          來(lái)源:愛(ài)玫瑰的小王子

          https://juejin.cn/post/6988364988427534349


          最后

          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對(duì)你有幫助,在看」是最大的支持
           》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持


          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  91人人看 | 自拍偷拍亚州第一 | 国产二线在线观看 | 日韩午夜 | 欧美做爱高潮白 |