[Day13] TS:什麼!這個 typeof 和我想的不一樣?
這是我們今天要聊的內容(不是業配),老樣的,如果你已經可以輕鬆看懂,歡迎直接左轉去看我同事 Ken 精彩的文章 — 「From State Machine to XState」。
Typeof Operator in JavaScript
多數 JavaScript 的開發者都知道,在 JS 中有 typeof operator 可以使用,使用上雖然有一些要留意的細節,但基本上 typeof
可以幫開發者判斷某個變數的型別:
// https://javascript.info/types#type-typeof
typeof undefined; // "undefined"
typeof 0; // "number"
typeof true; // "boolean"
typeof 'foo'; // "string"
typeof (() => {}); // "function"
typeof null; // "object"
Typeof Type Operator in TypeScript
但今天我們要聊的是 TS 中的 Typeof Type Operator,多一個 type 差很多。
這個 typeof
的用法其實非常簡單,我們知道在 TypeScript 中,即使沒有針對變數定義型別(Type Annotation),它還是會自動幫我們推導出該變數對應的型別,只要把滑鼠移到該變數上方,就可以看到 TS 自動幫變數推導出其可能的型別:
這裡 TypeScript 會自動把 conference 這個「變數」推導為如下的「型別」:
const conference: {
name: string;
year: number;
isAddToCalendar: boolean;
website: string;
};
但這個被自動推導出來的型別需要滑鼠移上去時才看得到,有沒有什麼方式能夠把這個「自動推導的結果」建立成一個可以被使用的型別呢?
有,就是 Typeof Type Operator,它的語法一樣就是 typeof
,後面可以接「變數」或「屬性」:
type Conference = typeof conference;
這個建立出來的型別 Conference
就會是剛剛一開始滑鼠移到變數 conference
上時顯示的結果:
那回到今天最開始的例子:
const conference = {
name: 'MOPCON',
year: 2021,
isAddToCalendar: true,
website: 'https://mopcon.org/2021/',
};
type ConferenceKeys = keyof typeof conference;
搭配在第 4 天時對 keyof
的了解,讀者應該可以想到 ConferenceKeys
的結果會是什麼。因為 typeof conference
是物件型別,所以 keyof typeof conference
就會是把所有該物件型別所有的 key 取出組成 Union Type:
讀者也許會好奇,不能直接用 keyof conference
就好,一定要在 conference
前面加上 typeof
嗎?學習 TS 的重點之一就是拿掉看看會發生什麼事,現在就讓我們拿掉原本的 typeof
看看:
這時候 TypeScript 報錯了,錯誤內容是:
'conference' refers to a value, but is being used as a type here. Did you mean 'typeof conference'?
意思是,conference
是 value,但它卻被當成 type 來使用。為什麼會有這個錯誤呢?
還記得 conference
本身是一個 JavaScript 中的變數而不是 TypeScript 中的型別嗎?因為 keyof
後面只能接的是「型別(Type)」,不能接 JavaScript 的「值(value)」,因此,一定會需要透過 typeof
先把 JS 的變數值轉成 TS 的型別後,才能使用 keyof
這個 operator。
區分 JavaScript 和 TypeScript 的 typeof
其實...也沒什麼好區分的。
雖然這兩個的語法都是 typeof
,但執行的時間點是完全不同的,JavaScript 的 typeof operator 會在程式執行時(runtime)呼叫,而 TypeScript 的 typeof type operator 則只有在 TypeScript 中「操作型別」時會用到,一旦把 .ts
編譯成 .js
後,到了 runtime 會無影無蹤(畢竟 JS 原生並沒有這種強型別的概念)。
可以分辨的出那個是 JavaScript 的 Typeof Operator,那個是 TypeScript 的 Typeof Type Operator 嗎?
const conference = {
name: 'MOPCON',
year: 2021,
isAddToCalendar: true,
website: 'https://mopcon.org/2021/',
};
type Conference = typeof conference;
const typeofConference = typeof conference;
補充:把 enum 的所有 key 取出成 union types
還記得在 Day11 中曾提到,透過 Template Literal Types 可以把 enum 的 values 取出變成聯集嗎?
enum MANUFACTURE {
APPLE = 'apple',
SAMSUNG = 'samsung',
GOOGLE = 'google',
SONY = 'sony',
}
type Manufacture = `${MANUFACTURE}`; // "apple" | "samsung" | "google" | "sony"
現在如果是想 要把 enum 的 key 取出變成 union types 的話,則可以使用到今天提的 Typeof Type Operator:
// Get all keys of enum
type ManufactureKeys = keyof typeof MANUFACTURE; // "APPLE" | "SAMSUNG" | "GOOGLE" | "SONY"
是不是很酷又很方便啊!現在讀者就可以根據需要取得 enum 的 keys union 或 values union 了。
範例程式碼
https://tsplay.dev/mAV3RW @ TypeScript Playground
工商時間
筆者今年將會在 MOPCON 2021 分享主題「用 Type 建立 Type:一起來當個 TypeScript 的型別魔術師」,提供給對 TypeScript 有興趣,又覺得每天追文章實在太辛苦的你來參考看看!但即使報名了 MOPCON 還是可以繼續追蹤鐵人賽啦!
參考資料
- Typeof Type Operator @ TypeScript > Type Manipulation