iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0
JavaScript

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

第14關:Get Return Type!TypeScript 預言家:看見返回型別

  • 分享至 

  • xImage
  •  

第14關:Get Return Type

關卡簡介

Implement the built-in ReturnType<T> generic without using it.

實現內建的 ReturnType<T> 泛型,而不使用內建的版本

任務說明:

const fn = (v: boolean) => {
  if (v)
    return 1
  else
    return 2
}

type a = MyReturnType<typeof fn> // should be "1 | 2"

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

type cases = [
  Expect<Equal<string, MyReturnType<() => string>>>,
  Expect<Equal<123, MyReturnType<() => 123>>>,
  Expect<Equal<ComplexObject, MyReturnType<() => ComplexObject>>>,
  Expect<Equal<Promise<boolean>, MyReturnType<() => Promise<boolean>>>>,
  Expect<Equal<() => 'foo', MyReturnType<() => () => 'foo'>>>,
  Expect<Equal<1 | 2, MyReturnType<typeof fn>>>,
  Expect<Equal<1 | 2, MyReturnType<typeof fn1>>>,
]

冒險指南:

從以下幾個方向來思考:

  • 推導返回型別 (Inferring the Return Type): 我們需要從函式型別中提取它的返回值 (return type)。這可以通過條件型別 (conditional types) 與 推導 (infer) 來實現,關鍵是如何正確地抓取返回型別。
  • 應對複合型函式 (Handling Nested or Complex Functions): 你可能會遇到返回值是複合型的函式,例如返回另一個函式。這時,你需要考慮如何處理這類型別。
  • 泛型與限制 (Generics and Constraints): 使用泛型限制 T 是一個函式型別。這樣才能保證你只處理函式,而不是其他型別,避免產生不必要的 never 型別。

通關方式:

解法:

type MyReturnType<T extends (...arg: any[]) => any> = T extends (...arg: any[]) => infer A ? A : never

細節分析:

  • 泛型參數 (Generic Parameters):

    • T extends (...args: any) => any:這裡 T 被明確限制為一個函式型別,它可以接受任何數量的參數,並且返回任意型別。這個限制確保我們的泛型 T 必須是函式型別,避免對非函式型別進行操作。
  • 條件型別與推斷 (Conditional Types and Inference):

    • 使用 T extends (...arg: any[]) => infer A,透過 infer 來推斷 T 函式的返回型別 A。如果 T 是函式型別,則會推斷並返回其返回值的型別;如果不是,則返回 never。這裡 infer 是關鍵,它負責提取返回值型別。
  • 停止條件 (Stopping Conditions):

    • 沒有遞迴的需求,但有隱含的停止條件:當 T 不是函式型別時,返回 never,防止處理非預期的型別。這樣的設計也處理了無參數函式的情況,讓 infer A 自動推斷空參數函式的返回值。

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

其他補充:

這是初版解法,想了想不夠好,為什麼呢?
缺乏明確的型別限制 (Lack of Clear Type Restriction)

type MyReturnType0<T> = T extends (...arg: any[]) => infer A ? A : never

在這段代碼中,雖然使用 MyParameters0<T> 也不會報錯,但這種寫法其實不夠嚴謹,原因在於對 T 的約束過於寬泛。在 MyParameters0<T>中,T 可以是任何型別,沒有任何限制或條件。也就是說,T 不一定非得是函式型別,這會導致該泛型對不應用於函式的型別也能通過編譯,但這不是我們的想要的行為。

(使用 typeof 看一下分別會返回什麼型別吧)
https://ithelp.ithome.com.tw/upload/images/20240928/20168789ZVRaBUW164.png

關鍵字補給:

未來補充 😭

總結:

本次介紹了 Get Return Type 的實作,下一關會挑戰 Omit,期待再相見!


上一篇
第13關:Parameters!TypeScript Expecto Patronum : 疾疾,參數現身
下一篇
第15關:Omit!TypeScript Expelliarmus:去去,屬性走
系列文
TypeScript Type Challenges 冒險篇章:30 天闖關之旅,type 簡單了?你確定?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言