slate 將 typescript 的型別擴充相關的內容都集合在 interfaces/custom-types.ts 這個 file 裡面。
這個 file 的 code 是由一名叫 thesunny 的 contributor 針對新版 slate 處理 custom types 的修改建議。
因為在之前的版本,開發者需要在每次使用 api call 時都得重複以 generic 的形式傳 custom-types ,這份 file 出現的目的正是讓開發者在開發 slate project 時只需定義一次 custom-types 就能一勞永逸。
這裡頭主要功能的部分只有短短地不到 10 行,卻利用了 interface
的 declaration merging 、 type
的 discriminating union 以及 unknown
的特性一次包辦了整個 type customizing 所需的一切功能,筆者當初看到的時候覺得真的是太厲害了!特地拉成一整篇文章與各位讀者分享。
我們先直接來看一下 custom-types.ts file 裡面的 code
/**
* Extendable Custom Types Interface
*/
type ExtendableTypes =
| 'Editor'
| 'Element'
| 'Text'
| 'Selection'
| 'Range'
| 'Point'
| 'InsertNodeOperation'
| 'InsertTextOperation'
| 'MergeNodeOperation'
| 'MoveNodeOperation'
| 'RemoveNodeOperation'
| 'RemoveTextOperation'
| 'SetNodeOperation'
| 'SetSelectionOperation'
| 'SplitNodeOperation'
export interface CustomTypes {
[key: string]: unknown
}
export type ExtendedType<
K extends ExtendableTypes,
B
> = unknown extends CustomTypes[K] ? B : CustomTypes[K]
ExtendableTypes
不難理解,它就是事先為可以進行擴充的 types 做好限制,在 ExtendedType
吃的第一個 generic type 有限制了它的範圍必須縮限在 ExtendableTypes 之中,這也是為什麼我們在 Day11 的 Example type 範例放進 ExtendedType
裡是不會過關的原因。
CustomTypes
是主要讓開發者定義 custom types 的 interface
,利用 interface
可以 extend 的特性讓開發者可以透過 declare module 等方式擴充。
拿 slate 提供的 example code 作為範例,引入下方的 file 後 CustomTypes
就會多了 Editor
、 Element
、 Text
這三組 keys 的定義:
declare module 'slate' {
interface CustomTypes {
Editor: CustomEditor
Element: CustomElement
Text: CustomText | EmptyText
}
}
最後是最精華的 ExtendedType
utility ,它吃了兩個 type generics 分別是:
ExtendableTypes
,負責比對 CustomTypes
中指定的 key 類型是否被擴充定義。CustomTypes
中被擴充定義的話會原封不動地回傳 B 。讓我們搭配最基本的 Text
type 協助我們介紹
/** text.ts */
export interface BaseText {
text: string
}
export type Text = ExtendedType<'Text', BaseText>
/** custom-types.ts */
export type ExtendedType<
K extends ExtendableTypes,
B
> = unknown extends CustomTypes[K] ? B : CustomTypes[K]
'Text'
與 BaseText
傳入 ExtendedType
以後,它首先會確認 'Text'
是否存在於 ExtendableTypes
裡。unknown extends CustomTypes[K]
的三元判斷式,這裏運用了 unknown
只會 extends unknown
這項特性,當判斷結果為 true 時代表開發者未在 CustomTypes
裡對 K 做擴充,回傳 base-type 給 Text
,反之則回傳 CustomTypes
裡 K
對應到的擴充內容給 Text
。最後再附上 Slate Github issue 的討論串,筆者在深入了解以前都只是照著 slate 提供的 example code 依樣畫葫蘆而已,一直將納悶放在心中。希望今天的分享也能讓讀者們體會筆者當時備受震懾的心情 XD
到這篇為止我們的 interface/ 章節終於要畫上句點了,接下來我們要先花些篇幅來聊聊 Immutable 這項議題,以及它、 Immer.js 、 Slate 之間的關係。
咱們明天見囉~