<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條件類型精讀與實(shí)踐

          共 4638字,需瀏覽 10分鐘

           ·

          2021-10-09 10:29

          來(lái)源 | http://www.fly63.com/


          在大多數(shù)程序中,我們必須根據(jù)輸入做出決策。TypeScript 也不例外,使用條件類型可以描述輸入類型與輸出類型之間的關(guān)系。
          本文同步首發(fā)在 個(gè)人博客 中,歡迎訂閱、交流。

          用于條件判斷時(shí)的 extends

          當(dāng) extends 用于表示條件判斷時(shí),可以總結(jié)出以下規(guī)律
          1. 若位于 extends 兩側(cè)的類型相同,則 extends 在語(yǔ)義上可理解為 === ,可以參考如下例子:
          type result1 = 'a' extends 'abc' ? true : false // falsetype result2 = 123 extends 1 ? true : false     // false
          1. 若位于 extends 右側(cè)的類型包含位于 extends 左側(cè)的類型(即狹窄類型 extends 寬泛類型)時(shí),結(jié)果為 true,反之為 false??梢詤⒖既缦吕?

          type result3 = string extends string | number ? true : false // true
          1. 當(dāng) extends 作用于對(duì)象時(shí),若在對(duì)象中指定的 key 越多,則其類型定義的范圍越狹窄??梢詤⒖既缦吕?

          type result4 = { a: true, b: false } extends { a: true } ? true : false // true

          在泛型類型中使用條件類型

          考慮如下 Demo 類型定義:

          type Demo<T, U> = T extends U ? never : T

          結(jié)合用于條件判斷時(shí)的 extends,可知 'a' | 'b' | 'c' extends 'a' 是 false, 因此 Demo<'a' | 'b' | 'c', 'a'> 結(jié)果是 'a' | 'b' | 'c' 么?

          查閱官網(wǎng) ,其中有提到:

          When conditional types act on a generic type, they become distributive when given a union type.

          即當(dāng)條件類型作用于泛型類型時(shí),聯(lián)合類型會(huì)被拆分使用。即 Demo<'a' | 'b' | 'c', 'a'> 會(huì)被拆分為 'a' extends 'a' 、 'b' extends 'a' 、 'c' extends 'a' 。用偽代碼表示類似于:

          function Demo(T, U) {  return T.map(val => {    if (val !== U) return val    return 'never'  })}
          Demo(['a', 'b', 'c'], 'a') // ['never', 'b', 'c']

          此外根據(jù) never 類型 的定義 —— never 類型可分配給每種類型,但是沒(méi)有類型可以分配給 never(除了 never 本身)。即 never | 'b' | 'c' 等價(jià)于 'b' | 'c' 。

          因此 Demo<'a' | 'b' | 'c', 'a'> 的結(jié)果并不是 'a' | 'b' | 'c' 而是 'b' | 'c' 。

          工具類型

          心細(xì)的讀者可能已經(jīng)發(fā)現(xiàn)了 Demo 類型的聲明過(guò)程其實(shí)就是 TypeScript 官方提供的工具類型中 Exclude<Type, ExcludedUnion> 的實(shí)現(xiàn)原理,其用于將聯(lián)合類型 ExcludedUnion 排除在 Type 類型之外。

          type T = Demo<'a' | 'b' | 'c', 'a'> // T: 'b' | 'c'

          基于 Demo 類型定義,進(jìn)一步地還可以實(shí)現(xiàn)官方工具類型中的 Omit<Type, Keys> ,其用于移除對(duì)象 Type

          中滿足 keys 類型的屬性值。

          type Omit<Type, Keys> = {  [P in Demo<keyof Type, Keys>]: Type<P>}
          interface Todo { title: string; description: string; completed: boolean;}
          type T = Omit<Todo, 'description'> // T: { title: string; completed: boolean }

          逃離艙

          如果想讓 Demo<'a' | 'b' | 'c', 'a'> 的結(jié)果為 'a' | 'b' | 'c' 是否可以實(shí)現(xiàn)呢? 根據(jù) 官網(wǎng) 描述:

          Typically, distributivity is the desired behavior. To avoid that behavior, you can surround each side of the extends keyword with square brackets.

          如果不想遍歷泛型中的每一個(gè)類型,可以用方括號(hào)將泛型給括起來(lái)以表示使用該泛型的整體部分。

          type Demo<T, U> = [T] extends [U] ? never : T
          // result 此時(shí)類型為 'a' | 'b' | 'c'type result = Demo<'a' | 'b' | 'c', 'a'>

          在箭頭函數(shù)中使用條件類型

          在箭頭函數(shù)中使用三元表達(dá)式時(shí),從左向右的閱讀習(xí)慣導(dǎo)致函數(shù)內(nèi)容區(qū)若不加括號(hào)則會(huì)讓使用方感到困惑。比如下方代碼中 x 是函數(shù)類型還是布爾類型呢?

          // The intent is not clear.var x = a => 1 ? true : false

          在 eslint 規(guī)則 no-confusing-arrow 中,推薦如下寫(xiě)法:

          var x = a => (1 ? true : false)

          在 TypeScript 的類型定義中,若在箭頭函數(shù)中使用 extends 也是同理,由于從左向右的閱讀習(xí)慣,也會(huì)導(dǎo)致閱讀者對(duì)類型代碼的執(zhí)行順序感到困惑。

          type Curry<P extends any[], R> =  (arg: Head<P>) => HasTail<P> extends true ? Curry<Tail<P>, R> : R

          因此在箭頭函數(shù)中使用 extends 建議加上括號(hào),對(duì)于進(jìn)行 code review 有很大的幫助。

          type Curry<P extends any[], R> =  (arg: Head<P>) => (HasTail<P> extends true ? Curry<Tail<P>, R> : R)

          結(jié)合類型推導(dǎo)使用條件類型

          在 TypeScript 中,一般會(huì)結(jié)合 extends 來(lái)使用類型推導(dǎo) infer 語(yǔ)法。使用它可以實(shí)現(xiàn)自動(dòng)推導(dǎo)類型的目的。

          比如用其來(lái)實(shí)現(xiàn)工具類型 ReturnType<Type> ,該工具類型用于返回函數(shù) Type 的返回類型。

          type ReturnType<T extends Function> = T extends (...args: any) => infer U ? U : never
          MyReturnType<() => string> // stringMyReturnType<() => Promise<boolean> // Promise<boolean>

          結(jié)合 extends 與類型推導(dǎo)還可以實(shí)現(xiàn)與數(shù)組相關(guān)的 Pop<T> 、 Shift<T> 、 Reverse<T> 工具類型。

          Pop<T>:

          type Pop<T extends any[]> = T extends [...infer ExceptLast, any] ? ExceptLast : never
          type T = Pop<[3, 2, 1]> // T: [3, 2]

          Shift<T>:

          type Shift<T extends any[]> = T extends [infer _, ...infer O] ? O : never
          type T = Shift<[3, 2, 1]> // T: [2, 1]

          Reverse<T>

          type Reverse<T> = T extends [infer F, ...infer Others]  ? [...Reverse<Others>, F]  : []
          type T = Reverse<['a', 'b']> // T: ['b', 'a']

          使用條件類型來(lái)判斷兩個(gè)類型完全相等

          我們也可以使用條件類型來(lái)判斷 A、B 兩個(gè)類型是否完全相等。當(dāng)前社區(qū)上主要有兩種方案:

          方案一: 參考 issue 。

          export type Equal1<T, S> =  [T] extends [S] ? (    [S] extends [T] ? true : false  ) : false

          目前該方案的唯一缺點(diǎn)是會(huì)將 any 類型與其它任何類型判為相等。

          type T = Equal1<{x:any}, {x:number}> // T: true

          方案二: 參考 issue 。

          export type Equal2<X, Y> =  (<T>() => T extends X ? 1 : 2) extends  (<U>() => U extends Y ? 1 : 2) ? true : false

          目前該方案的唯一缺點(diǎn)是在對(duì)交叉類型的處理上有一點(diǎn)瑕疵。

          type T = Equal2<{x:1} & {y:2}, {x:1, y:2}> // false

          以上兩種判斷類型相等的方法見(jiàn)仁見(jiàn)智,筆者在此拋磚引玉。



          學(xué)習(xí)更多技能

          請(qǐng)點(diǎn)擊下方公眾號(hào)

          瀏覽 38
          點(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>
                  麻豆中文字幕 | 黄色视频网站国产 | 日本日p视频 | 欧美在线se | 自拍偷拍亚州第一 |