iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
JavaScript

TypeScript Type Challenges 冒險篇章:30 天闖關之旅,type 簡單了?你確定?系列 第 8

第8關:Exclude!TypeScript稽查員:不合格的全都出去

  • 分享至 

  • xImage
  •  

第8關:Exclude

關卡簡介

Implement the built-in Exclude<T, U>
(Exclude from T those types that are assignable to U)

實現內建的 Exclude<T, U>
從 T 中排除那些可以賦值給 U 的類型

任務說明:

type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'

接下來,你的任務是讓下面的type cases測試通過:

type cases = [
  Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a'>, 'b' | 'c'>>,
  Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a' | 'b'>, 'c'>>,
  Expect<Equal<MyExclude<string | number | (() => void), Function>, string | number>>,
]

冒險指南:

在這一關中,我們可以從以下幾個方向來思考:

  1. 如何讓TU逐個比較?

我們將會用到:

  1. extends:用於條件類型中,檢查一個類型是否符合另一個類型的約束。如果符合,則返回相應的類型;否則返回另一個類型。常用於檢查類型的兼容性。
  2. Distributive Conditional Types:當條件類型應用於 union type (聯合類型) 時,會對該聯合類型的每個成員單獨執行條件判斷,這讓我們可以逐一處理聯合類型中的成員。
extends 在第三關已經介紹過,可以回到第三關參考喔!

通關方式:

解法:

type MyExclude<T, U> = T extends U ? never : T;

細節分析:

  • T extends U ? never : T
    • (Conditional type):這是 TypeScript 中的一個基本語法,用於根據條件返回不同的類型。當 T 是 U 的子類型時,返回 never;如果不是,則返回 T 本身。
      -T extends U:這部分檢查 T 中的每個成員是否可以賦值給 U。如果某個成員符合條件,則不包括該成員(返回 never),這樣最終的結果就只會包含不符合條件的成員。

這樣,我們就能順利通過測試啦 🎉 😭 🎉

關鍵字補給:

  • Distributive Conditional Types :
    • 介紹:
      “Distributive” :
      An operation that produces the same result when applied to an entire expression as when applied individually to each part and then combining the results.

      (such as multiplication in a(b + c) = ab + ac)

    • 範例:
      When conditional types act on a generic type, they become distributive when given a union type. For example, take the following:

         type ToArray<Type> = Type extends any ? Type[] : never;
      

      If we plug a union type into ToArray, then the conditional type will be applied to each member of that union.

         type ToArray<Type> = Type extends any ? Type[] : never;
      
         type StrArrOrNumArr = ToArray<string | number>;
                     //^? type StrArrOrNumArr = string[] | number[]
      

      What happens here is that ToArray distributes on:

      
         string | number;
      
      

      and maps over each member type of the union, to what is effectively:

         ToArray<string> | ToArray<number>;
      

      which leaves us with:

          string[] | number[];
      

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

         type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
      
         // 'ArrOfStrOrNum' is no longer a union.
         type ArrOfStrOrNum = ToArrayNonDist<string | number>;
                 //^? type ArrOfStrOrNum = (string | number)[]
      

      Non-Distributive Conditional Types
      - Tuple Type: By wrapping the type Type in square brackets ([]), you are creating a tuple type [Type].
      - Non-Distributive: TypeScript no longer sees Type as a naked type parameter. Instead, it sees [Type] as a single entity, a tuple type containing Type.
      - Single Evaluation Since [Type] is not a union, the conditional type extends [any] is evaluated as a whole, not distributed over individual union members.
      - Result The result is a non-distributive application of the conditional type, leading to the entire union type being treated as a single entity within the array.

總結:

本次介紹了 Exclude 的實作,下一關會挑戰 If,期待再相見!


上一篇
第7關:Length of Tuple!TypeScript的三長兩短,給它量,很放心
下一篇
第9關:If!TypeScript可惜沒如果:boolean世界,非黑即白
系列文
TypeScript Type Challenges 冒險篇章:30 天闖關之旅,type 簡單了?你確定?13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言