用 Enum 提升TypeScript代碼的可讀性

Enum

Enum 是在 TypeScript 中新增的語法,也叫作枚舉,通常用它來管理多個相同系列的常量(即不能被修改的變量),用於狀態的判斷。javascript

在 Web 中比較常見的狀態判斷,是在處理請求時,要針對不一樣的響應狀態碼作對應的處理:前端

const handleResponseStatus = (status: number): void => {
  switch (status) {
    case 200: // 請求成功時
      // Do something...
      break;
    case 400: // 請求失敗時
      // Do something...
      break;
    default:
      throw (new Error('No have status code!'));
  }
};

但由於響應狀態碼都是預先定義好的,因此沒什麼爭議,代碼寫成這樣看也很正常,可是若是後端在服務器發生錯誤時自定義了一些編碼,並告訴前端,這些代碼都表明什麼錯誤,那麼上面的函數可能會變成這樣:java

const handleWrongStatus = (status: string): void => {
  switch (status) {
    case 'A':
      // Do something...
      break;
    case 'B':
      // Do something...
      break;
    case 'C':
      // Do something...
      break;
    default:
      throw (new Error('No have wrong code!'));
  }
};

若是是這種代碼,別說是剛接手的人,就算是你本身兩星期前寫的,恐怕不去翻文檔也想不起它們都表明什麼了吧。程序員

可是若是善用 Enum ,就能夠避免上述發生的狀況。面試

基本用法

先來看看 Enum 該怎麼定義,它和 Object 的用法很像:typescript

enum requestStatusCodes {
  error,
  success,
}

不須要在內容與名稱之間加等號,直接在大括號內敘述該 Enum 中具備哪些變量,與其說是變量,不如說是常量更恰當些,由於在 Enum 中的值是不可修改的,因此也沒必要擔憂這些定義好的規則會在代碼執行的過程當中發生改變,致使執行錯誤。segmentfault

而既然 Enum 是用來定義同一個系列常量的,那這些常量應該都能維護特定的值。沒錯,在 Enum 中的每一個常量,均可以經過 = 來指定具體的值 。後端

但若是是像前面的 requestStatusCodes ,沒有爲 errorsuccess 指定具體的值也不會出錯,由於 TypeScript 會從 0 開始自動遞增定義值,因此簽名的 requestStatusCodes 會和下面的結果相同:服務器

enum requestStatusCodes {
  error = 0,
  success = 1,
}console.log(requestStatusCodes.error) // 0
console.log(requestStatusCodes.success) // 1

除了數字外,也能夠定義爲字串:微信

enum requestWrongCodes {
  missingParameter = 'A',
  wrongParameter = 'B',
  invalidToken = 'C',
}console.log(requestWrongCodes.wrongParameter) // 'B'

固然也能夠在一個 enum 中設定不一樣的類型,但這樣一點意義都沒有:

enum requestStatusCodes {
  error = 0,
  success = 'OK',
}

瞭解基本的 Enum 怎麼定義後,接着就來改寫前面代碼中的 handleResponseStatushandleWrongStatus ,讓它們在語義上可以更明確。

首先用 Enum 定義二者的狀態描述:

enum requestStatusCodes {
  error = 400,
  success = 200,
}

enum requestWrongCodes {
  missingParameter = 'A',
  wrongParameterType = 'B',
  invalidToken = 'C',
}

而後修改 handleResponseStatushandleWrongStatus 中的 Switch 判斷:

const handleResponseStatus = (status: number): void => {
  switch (status) {
    case requestStatusCodes.success:
      // Do something...
      break;
    case requestStatusCodes.error:
      // Do something...
      break;
    default:
      throw (new Error('No have status code!'));
  }
};

const handleWrongStatus = (status: string): void => {
  // 若是以爲 requestWrongCodes.missingParameter 太長了,也能夠用如下方式:
  const { missingParameter, wrongParameterType, invalidToken, } = requestWrongCodes;
  switch (status) {
    case missingParameter:
      // Do something...
      break;
    case wrongParameterType:
      // Do something...
      break;
    case invalidToken:
      // Do something...
      break;
    default:
      throw (new Error('No have wrong code!'));
  }
};

修改後的代碼就變得直觀多了,由於狀態碼都被放到了 Enum 中統一管理,因此就能用常量名來表明它們,以後無論過了多久,能夠明確的知道這裏再作什麼,甚至連註解或文檔都不用寫了,由於代碼就是最好的文檔。

善用 Enum 能使代碼絕對是不可或缺的,但就算沒使用 TypeScript 也別灰心,由於 TypeScript 最終會被轉換爲 JavaScript ,那來看看如何直接用 JavaScript 實現 Enum 吧!

用原生 JavaScript 實現 Enum

在前面說過 Enum 很像 Object ,若是研究一下 Enum 被編譯成 javascript 以後的代碼,就會發現還真的是 Object。

Enum 被編譯後會變成 Key 和 Value 反向對應的對象,這樣看起來很是簡單,爲了方便使用,下面把它的編譯方式寫成一個函數:

const newEnum = (descriptions) => {
  const result = {};
  Object.keys(descriptions).forEach((description) => {
    result[result[description] = descriptions[description]] = description;
  });
  return result;
};

const responseStatus = newEnum({
  error: 400,
  success: 200,
});

// { '200': 'success', '400': 'error', error: 400, success: 200 }
console.log(responseStatus);

雖然獲得的結果相同,可是喪失了 Enum 中最難得的常量特點,若是不能讓它變成不可修改,那就有可能會在代碼裏不經意地改動它,致使執行結果可能出錯,因而能夠在最後利用 Object.freeze() ,讓外部操做沒法新增、刪除或從新定義任何 Property :

const newEnum = (descriptions) => {
  const result = {};
  Object.keys(descriptions).forEach((description) => {
    result[result[description] = descriptions[description]] = description;
  });
  return Object.freeze(result);
};

const responseStatus = newEnum({
  error: 400,
  success: 200,
});

// 即便不當心修改了
responseStatus['200'] = 'aaaaaaaa';

// 仍然是 { '200': 'success', '400': 'error', error: 400, success: 200 }
console.log(responseStatus);

這樣就能簡單在 JavaScript 中實現 Enum 了。

const Enum 的用法

從前面的 JavaScript 代碼中能夠看到 Enum 編譯事後會變成 Key 和 Value 互相對應的 Object ,也就是說不論是用 Key 仍是Value 均可以取出對應的值,

可是若是用 const 聲明 Enum ,編譯以後就不會產生 Object。

直接看例子,假設我把 responseStateconst 從新生命,且也是以 handleResponseStatus 使用該 Enum 作判斷:

enum responseStatus {
  error = 400,
  success = 200,
}

const handleResponseStatus = (status: number): void => {
  switch (status) {
    case responseStatus.success:
      console.log('請求成功!');
      break;
    case responseStatus.error:
      console.log('請求失敗!');
      break;
    default:
      throw (new Error('No have status code!'));
  }
};

看起來一切正常,不過在編譯後的 JavaScript 中,會發現 Enum 並無產生 Object ,而是直接用 const 聲明在 Enum 中的值。

const 聲明 Enum 有幾個好處:

  1. 假設要用到的 Enum 很是多,那在執行時就會不停地使用 IIFE 產生 Object 將 Key 和 Value 綁定到 Object,會形成一些效率上的損失,也會增長內存,可是 const 並不會產生 Object ,也就不會有以上的問題。
  2. 就算到的 Enum 很少,判斷時也須要一直從 Object 中找出對應的值,而若是是用 const 聲明 Enum ,在編譯成 JS 時就將聲明的值直接放入判斷中。

不過這樣也就無法從 Enum 中反向取值了,由於它並不會產生對象:

const enum responseStatus {
  error = 400,
  success = 200,
}// 會出錯,由於已經沒有對象可供查找了
console.log(responseStatus[400])// 但這個不會有問題,由於編譯的時候會直接填值
console.log(responseStatus.error)// 編譯後:
// console.log(400)

173382ede7319973.gif


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章


歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索