前端常見的枚舉管理方式指南
寫在前面
前端里的“枚舉”,本質(zhì)上是在解決以下問題:
- 限制狀態(tài)范圍
- 提高可讀性
- 提高類型安全
- 避免魔法字符串
- 方便 UI 映射
- 方便 AI 理解
下面作者為同學(xué)們總結(jié)了前端領(lǐng)域常見的枚舉方式:
Typescript 的 enum 枚舉類型
enum 是 Typescript 原生支持的枚舉類型。許多同學(xué)不知道的是,枚舉的定義方式是有區(qū)別的,定義方式會決定 enum 的應(yīng)用方式,分為 enum 和 const enum
enum
支持定義數(shù)字和字符串,定義/使用方式如下:
enum Status {
Pending,
Success,
Failed
}
Status.Pending // 0
enum Status {
Pending = 'pending',
Success = 'success',
Failed = 'failed'
}
Status.Pending // pending
數(shù)字類型支持反向映射,如:
Status[Status.Pending] // Pending
字符串類型不支持反向映射,因?yàn)?enum 編譯后會輸出大致這樣的代碼:
// input
enum Status {
Pending,
Success,
Failed
}
// output
const Status = {
Pending: 0,
Success: 1,
Failed: 2,
0: "Pending",
1: "Success",
2: "Failed"
}
字符串會在設(shè)計層面有很多限制,這里隨便舉個例子:
// input
enum Vector {
X = 'Y',
Y = 'Z',
Z = 'X'
}
// output 命名空間已經(jīng)明顯亂掉了, 假設(shè)訪問 Vector['X'],都不知道訪問的是哪一個。
const Vector = {
X: 'Y',
Y: 'Z',
Z: 'X',
Y: 'X',
Z: 'Y',
X: 'Z'
}
const enum 枚舉
上文的 enum 是包含 運(yùn)行時 的,Typescript 會編譯出一個 Javascript 對象,但對 bundle 尺寸和性能有要求的庫,需要一種更輕的枚舉類型,這就是后來 Typescript 團(tuán)隊(duì)支持的 const enum 特性。定義/使用方式如下:
const enum Status {
Pending,
Success,
Failed
}
Status.Pending // 0
它不支持雙向映射,因?yàn)?ts 會在編譯時將 Status 抹去,只把 Status.Pending 的調(diào)用直接替換成字面量 0。
對象枚舉
絕大部分的前端工程師的工作還是以業(yè)務(wù)為主,更多只是單純的尋求 好用 + 實(shí)用 + 類型安全,基本不會在意 bundle 尺寸,實(shí)際上對于 Web App 來說,枚舉也不會帶來明顯的 bundle 尺寸增長。 這也催生出了對象枚舉的定義方式,優(yōu)勢在于開發(fā)者可以自由的定義映射,類型使用也更加明確簡單。
const Status = {
Pending: 'pending',
Success: 'success',
Failed: 'failed'
} as const
// as const 是必須的,影響下面的 type Status,對 as const 感興趣的同學(xué)可以去問一下 AI,這里就不展開了。
const StatusLabels = {
[Status.Pending]: '準(zhǔn)備中',
[Status.Success]: '成功',
[Status.Failed]: '失敗',
}
// 這個類型工具可以提取成通用工具
type ValueOf<T> = T[keyof T]
type Status = ValueOf<typeof Status> // 'pending' | 'success' | 'failed'
Status.Pending // 'pending'
StatusLabels[Status.Pending] // '準(zhǔn)備中'
enum-plus
對象枚舉雖然一定程度的解決了業(yè)務(wù)問題,但定義起來實(shí)在有點(diǎn)復(fù)雜,雖然這對于 AI 來說不是什么難事,但枚舉的信息密度非常低,不夠內(nèi)聚,關(guān)注點(diǎn)不夠聚焦,AI 也經(jīng)常犯迷糊。 但好在開發(fā)社區(qū)也有解決方案,比如 enum-plus。
import { Enum } from 'enum-plus'
const WeekEnum = Enum({
Sunday: { value: 0, label: 'I love Sunday' },
Monday: { value: 1, label: 'I hate Monday' },
});
WeekEnum.Sunday; // 0
WeekEnum.items[0].key; // 'Sunday'
WeekEnum.items[0].label; // 'I love Sunday'
作者找到這個庫的時候也是想要直接集成到項(xiàng)目里的,因?yàn)樗δ芊浅XS富,提供了 非常非常多 的 api。但同時我也覺得 api 過于復(fù)雜了,并且似乎類型約束相對松散,無法直接完成遷移(除非犧牲類型安全)比如:
import { Enum } from 'enum-plus'
const WeekEnum = Enum({
Sunday: { value: 0, label: 'I love Sunday' },
Monday: { value: 1, label: 'I hate Monday' },
})
console.log(WeekEnum.Sunday)
// 這里不會報類型錯誤,但 `2` 不是一個合法的枚舉值,enum-plus 在這種情況下會返回 undefined,雖然也是合理的,但作者更傾向于嚴(yán)格一些類型約定。
console.log(WeekEnum.label(2))
enumOf
最后是作者自己的版本,個人比較滿意。實(shí)現(xiàn)較為輕量,api 也比較簡單,并且類型安全,國際化支持也非常容易,同時開源并集成到了 rattail 工具庫里,有需要的同學(xué)可以自取。下面是常用案例。
import { enumOf } from 'rattail'
const Status = enumOf({
Success: { value: 0, label: 'Success' },
// 字段支持函數(shù)返回,國際化支持很簡單。
Warning: { value: 1, label: () => t('global.warning') },
})
Status.Success // 0
Status.label(Status.Success) // 'Success'
Status.options()
/*
[
{ value: 0, label: 'Success'},
{ value: 1, label: 'Warning' },
]
*/
Status.values() // [0, 1]
Status.labels() // ['Success', 'Warning']
你也可以借助 rattail 內(nèi)置的函數(shù)去調(diào)整 .options() 返回的數(shù)據(jù)結(jié)構(gòu),這個在業(yè)務(wù)開發(fā)里很常見。
import { rekey } from 'rattail'
Status.options().map(option => rekey(option, { value: 'key' }))
/*
[
{ key: 0, label: 'Success'},
{ key: 1, label: 'Warning' },
]
*/
你也可以選擇擴(kuò)展更多字段,它們都會有完善的類型推導(dǎo)。
const Status = enumOf({
Success: { value: 1, label: 'Success', color: 'green' },
Warning: { value: 2, label: 'Warning', color: 'orange' },
})
Status.option(Status.Success).color // 'green'
寫在最后
到此這篇關(guān)于前端常見的枚舉管理方式指南的文章就介紹到這了,更多相關(guān)前端枚舉管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
layer子層給父層頁面元素賦值,以達(dá)到向父層頁面?zhèn)髦档男Ч麑?shí)例
下面小編就為大家?guī)硪黄猯ayer子層給父層頁面元素賦值,以達(dá)到向父層頁面?zhèn)髦档男Ч麑?shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09
用JavaScript 判斷用戶使用的是 IE6 還是 IE7
判斷IE瀏覽器的腳本,方便根據(jù)瀏覽器不懂,支持不同的代碼的分別調(diào)用。2008-01-01
bootstrapValidator 重新啟用提交按鈕的方法
bootstrapValidator 使用中,由于字段檢查等原因,致使提交按鈕失效。如何重新啟用提交按鈕呢?下面小編給大家介紹下bootstrapValidator 重新啟用提交按鈕的方法,需要的朋友可以參考下2017-02-02

