[Day19] TS:什麼!泛型的參數還能有預設值?
今天這個範例是來自第三方套件 utility-types,在有了前幾天的知識後,讓我們來試著了解這個 Utility Type 是如何實作的吧!如果你已經可以輕鬆看懂,歡迎直接左轉去看我隊友們的精彩文章!
Optional 的用法
要把物件型別中的屬性全部變成 Optional 的話,在 Day16 時曾經提過可以使用官方內建的 Partial
,但假設今天只想要讓這個物件型別中的部分屬性變成 Optional 的話,就可以用這裡的這個 Optional<T, K>
。
備註:這裡的 Optional 並不是 TypeScript 中內建的 Utility Type,讀者如果要使用的話,記得要先複製這個 Utility Type 的原始碼到程式碼中。
舉例來說,現在有一個型別 Conference
:
type Conference = {
name: string;
year: number;
isAddToCalendar: boolean;
website: string;
};
後來發現 Conference
這個型別中,year
和 isAddToCalendar
都可以省略(Optional),只有名稱和網址是必填的,這時候就可以用 Optional 來達到:
type ConferenceWithOptional = Optional<Conference, 'year' | 'isAddToCalendar'>;
這時候這個 ConferenceWithOptional
就會變成是:
// year 和 isAddToCalendar 變成 optional 的
type ConferenceWithOptional = {
name: string;
year?: number;
isAddToCalendar?: boolean;
website: string;
};
但這個 Optional 還有一個蠻特別的地方,它也可以只帶入物件型別給它就好,而不告訴它那些屬性 Key 是要變成 optional 的:
// 沒有帶入 Optional 的第二個參數
type ConferenceWithAllOptional = Optional<Conference>;
這時候 ConferenceWithAllOptional
它「預設」就會把該物件型別中的所有 Key 都變成 Optional 的了:
// 預設會把所有屬性都變 Optional
type ConferenceWithAllOptional = {
name?: string;
year?: number;
isAddToCalendar?: boolean;
website?: string;
};
這裡我們發現兩個重要的點:
- 過去的 Utility Type 只要定義了幾個泛型參數,開發者就需要帶入幾個參數進去,不能多也不能少,但這裡卻可以帶一個參數或帶入兩個參數都可以。
- 多了「預設值」的概念,如果沒給所有參數,預設會把所有 Key 都變成 optional。
很特別吧!讓我們來理解看看它是怎麼被實作的吧!
理解 Optional 的實作
Optional
這個 Utility Type 的原始碼如下:
type Optional<T extends object, K extends keyof T = keyof T> = Omit<T, K> &
Partial<Pick<T, K>>;
要了解原始碼,最重要的就是要知道如何做出正確的斷句,讓我們先把注意力放到 =
的前面:
沒錯,這一長串都是 =
前面,從 <T extends ...>
開始,可以理解到 Optional
它吃兩個參數 T
和 K
:
在 Day03 中我們提過這是屬於泛型限制的寫法,所以 T extends object
就是 T
需要是 object
的子集合;後面的 K extends keyof T = keyof T
好像出現了我們不曾看過的語法,沒錯!這就是今天的重點「泛型參數預設值(Generic parameter defaults)」。