為什么TypeScript的Enum會出現(xiàn)問題
TypeScript引入了很多靜態(tài)編譯語言的特性,比如class(現(xiàn)在是JavaScript的一部分了),interface, generics和union types等。
但是今天有一個類型需要著重討論下,這就是enum。
對于很多的靜態(tài)語言來說,枚舉是一個很非常常見的語言特性。比如,c,c#,java和swift。枚舉就是你在代碼里可以用的一組常量。
我們用TypeScript來新建一個enum來代表一周的幾天:
enum DayOfWeek { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
這個枚舉使用enum關(guān)鍵字聲明,后面跟著DayOfWeek名稱。然后我們定義枚舉里可以使用的常量。
現(xiàn)在我們定義一個方法,接受這個枚舉類型的參數(shù),來判斷傳入的參數(shù)是不是周末。
function isItTheWeekend(day: DayOfWeek) { switch (day) { case DayOfWeek.Sunday: case DayOfWeek.Saturday: return true; default: return false; } }
最后,我們可以這要用:
console.log(isItTheWeekend(DayOfWeek.Monday)); // log: false
對于消除程序里的魔法字符串來說,這是一個非常有用的方法。
但是,事情遠不是我們想的這么簡單。下面的代碼調(diào)用會在TypeScript編譯之后得到什么呢?
console.log(isItTheWeekend(2)); // is this valid?
知道結(jié)果你會嚇一跳。這樣的調(diào)用是符合TypeScript規(guī)則的,編譯器也會順利編譯。
發(fā)生了什么呢?
上面的情況可能會讓你認為你發(fā)現(xiàn)了一個TypeScript的bug。其實TypeScript就是這么設(shè)計的。
我們這里新建了一個數(shù)字枚舉,而且我們可以在TypeScript Playground里看看編譯出來的結(jié)果是什么:
var DayOfWeek; (function (DayOfWeek) { DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday"; DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday"; DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday"; DayOfWeek[DayOfWeek["Wednesday"] = 3] = "Wednesday"; DayOfWeek[DayOfWeek["Thursday"] = 4] = "Thursday"; DayOfWeek[DayOfWeek["Friday"] = 5] = "Friday"; DayOfWeek[DayOfWeek["Saturday"] = 6] = "Saturday"; })(DayOfWeek || (DayOfWeek = {}));
運行結(jié)果是:
事實上枚舉就是一個JavaScript對象。
這個對象的屬性就是根據(jù)我進定義的枚舉常量生成,還根據(jù)定義的順序生成了對應(yīng)的數(shù)字(順序,Sunday是0,Saturday是6)。這個對象也有數(shù)字作為key,對應(yīng)的常量字符串作為值的屬性。
因此,我們可以給上面的方法傳入數(shù)字,數(shù)字映射到對應(yīng)的枚舉值。枚舉既是一個數(shù)字常量也是一個字符串常量。
什么時候用
如果一個方法接收一個枚舉類型參數(shù),但是一個任意的數(shù)字就可以通過編譯的話。這樣的結(jié)果顯然破壞了TypeScript構(gòu)建的類型安全體系。這什么時候可以用呢?
假設(shè)你有一個服務(wù)返回一個JSON串,你想把這個串建模,對應(yīng)的某個屬性是一個枚舉。
在你的數(shù)據(jù)庫里存的是數(shù)字。定義一個TypeScript枚舉可以很容易解決這個問題:
const day: DayOfWeek = 3;
這個在賦值時執(zhí)行的顯示的類型轉(zhuǎn)換會把數(shù)字轉(zhuǎn)換成枚舉的對應(yīng)常量。也就是說我們在代碼里使用這個枚舉會讓代碼更容易讀懂。
控制枚舉的數(shù)字
枚舉的成員對應(yīng)的數(shù)字是根據(jù)枚舉常量定義的順序生成的。那我們是否可以控制這個數(shù)字的值呢?是可以的。
enum FileState { Read = 1, Write = 2 }
只是描述一個文件可能的狀態(tài)的枚舉。
它可能是讀也可能是寫狀態(tài),我們顯示的定義了枚舉值?,F(xiàn)在就很明確什么樣的值是合理的,因為顯示定義了。
Bit值
但是還有另一個情況很有用,位值(Bit)。
我們再來看一下這個FileState枚舉,給它添加一個新的枚舉值ReadWrite:
enum FileState { Read = 1, Write = 2, ReadWrite = 3 }
之后假設(shè)有一個方法接受這個類型的參數(shù):
const file = await getFile("/path/to/file", FileState.Read | FileState.Write);
我們在FileState上使用了|操作符。這樣我們可以使用位運算來獲得一個新的枚舉值。在這個例子里面就是3,ReadWrite的值。
事實上,我們可以寫的更清楚一些:
enum FileState { Read = 1, Write = 2, ReadWrite = Read | Write }
這個ReadWrite的值不是寫死的,而是位運算得到的。
但是再這樣使用枚舉的時候要多加小心。
如下的枚舉:
enum Foo { A = 1, B = 2, C = 3, D = 4, E = 5 }
如果要得到E(或者5),可以位運算得到么:Foo.A | Foo.D or Foo.B | Foo.C?
所以如果要用枚舉值做位運算,那么明確如何得到這個值。
控制索引
一般情況下,每個枚舉值都會有一個默認的數(shù)字值。如果需要也可以明確的給這些枚舉值賦值。另外,還可以給某部分枚舉賦值:
enum DayOfWeek { Sunday, Monday, Tuesday, Wednesday = 10, Thursday, Friday, Saturday }
前幾個值是按照位置賦值,Sunday到TuesDay是0到2.之后在Wednesday給了一個新值,從這開始每個值都遞增1. 這就可能會出現(xiàn)問題了:
enum DayOfWeek { Sunday, Monday, Tuesday, Wednesday = 10, Thursday = 2, Friday, Saturday }
Tuesday賦值為2,生成的JavaScript是什么樣子呢:
var DayOfWeek; (function (DayOfWeek) { DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday"; DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday"; DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday"; DayOfWeek[DayOfWeek["Wednesday"] = 10] = "Wednesday"; DayOfWeek[DayOfWeek["Thursday"] = 2] = "Thursday"; DayOfWeek[DayOfWeek["Friday"] = 3] = "Friday"; DayOfWeek[DayOfWeek["Saturday"] = 4] = "Saturday"; })(DayOfWeek || (DayOfWeek = {}));
看起來Tuesday和Thursday的數(shù)值都是2。
所以,需要顯示的設(shè)定數(shù)值。
非數(shù)字枚舉
目前為止,我們只討論了數(shù)值枚舉,但是枚舉的值不一定非的是數(shù)字。它也可以是任何常量或者計算值:
enum DayOfWeek { Sunday = "Sun", Monday = "Mon", Tuesday = "Tues", Wednesday = "Wed", Thursday = "Thurs", Friday = "Fri", Saturday = "Sat" }
現(xiàn)在就不能給isItTheWeekend方法穿數(shù)字參數(shù)了。這個枚舉已經(jīng)不再是數(shù)字枚舉。然而,我們也不能傳任意字符串進去,因為枚舉知道什么樣的值才是合理的。
這樣也帶來另外一個問題:
const day: DayOfWeek = "Mon";
這樣是行不通的。
字符串并不能直接給枚舉賦值,而是需要一個顯示的類型轉(zhuǎn)換:
const day = "Mon" as DayOfWeek;
能不能給它賦其他值呢?事實上枚舉可以有很多類型的值:
enum Confusing { A, B = 1, C = 1 << 8, D = 1 + 2, E = "Hello World".length }
這個例子的枚舉值都是數(shù)字。但是這些數(shù)字值可以直接賦值,也可以是計算值,或者是字符串的length屬性。如果都是常量的話,那么就可以是多種類型的值:
enum MoreConfusion { A, B = 2, C = "C" }
這種情況就很難讓人理解枚舉后面的數(shù)據(jù)是怎么工作的。所以,最好不要用這樣的枚舉。
結(jié)論
TypeScript的枚舉是對JavaScript的一個很好地補充,使用得當(dāng)將非常有用。它將有助于清理代碼中存在的魔術(shù)值(magic values)字符串、數(shù)字。而且它是類型安全的。
到此這篇關(guān)于為什么TypeScript的Enum會出現(xiàn)問題的文章就介紹到這了,更多相關(guān)TypeScript Enum內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序授權(quán)登錄及解密unionId出錯的方法
這篇文章主要介紹了微信小程序授權(quán)登錄及解密unionId出錯的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09使用post方法實現(xiàn)json往返傳輸數(shù)據(jù)的方法
今天小編就為大家分享一篇關(guān)于使用post方法實現(xiàn)json往返傳輸數(shù)據(jù)的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03JS+JSP通過img標簽調(diào)用實現(xiàn)靜態(tài)頁面訪問次數(shù)統(tǒng)計的方法
這篇文章主要介紹了JS+JSP通過img標簽調(diào)用實現(xiàn)靜態(tài)頁面訪問次數(shù)統(tǒng)計的方法,基于JavaScript動態(tài)調(diào)用jsp頁面通過對TXT文本文件的讀寫實現(xiàn)統(tǒng)計訪問次數(shù)的功能,需要的朋友可以參考下2015-12-12javascript 延遲加載技術(shù)(lazyload)簡單實現(xiàn)
延遲加載技術(shù)(簡稱lazyload)并不是新技術(shù), 它是js程序員對網(wǎng)頁性能優(yōu)化的一種方案.2011-01-01js刪除數(shù)組中的元素delete和splice的區(qū)別詳解
下面小編就為大家分享一篇js刪除數(shù)組中的元素delete和splice的區(qū)別詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02