Void&Symbol in TypeScript(譯)

1. Void

void in JavaScript

let i = void 2; // i === undefined
複製代碼

Why 爲何須要void?javascript

由於早期js裏,人們能夠重寫undefined,而後賦予真正的值給它,而void老是返回真正的undefined。java

  • void在JS裏是一個相似單目運算符的存在。
  • void提供了一種當即執行函數的方式,能夠不須要藉助函數表達式。
void function() {
    console.log('what')
}()
複製代碼
  • 使用void能夠避免污染全局變量。
void function aRecursion(i) {
  if(i > 0) {
    console.log(i--)
    aRecursion(i)
  }
}(3)
console.log(typeof aRecursion) // undefined
複製代碼

函數aRecursion執行完後當即釋放, 全局做用域也沒法獲取到這個引用。由於void老是能夠執行它後面的表達式。typescript

  • 使用void執行函數會同時執行傳入的回調。
function middleware(nextcb) {
    if(conditionApplies()){
        return void nextcb();
    }
}
複製代碼
  • void最重要的用例: 就是保證函數永遠返回undefined,由於返回值是動態地,返回值可能會根據方法內的邏輯返回,可是void卻能夠100%保證返回undefined
button.onclick = () => void doSomething();
複製代碼

void in TypeScript

定義安全

  1. void在ts裏面則是一種基本類型。
  2. void在ts也是undefined的子類型。

用法bash

  • 在ts中,若是函數沒寫返回類型,或者沒有return語句,自動返回undefined
function () {
    console.log('xx')
}
複製代碼
  • void能夠做爲類型聲明中的參數。
declare function iTakeNoParameters(x: void): void 複製代碼

此時只有undefined能夠做爲參數傳入函數

iTakeNoParameters(undefined) // 
iTakeNoParameters(void 2) // 
複製代碼
  • void的最大特色

Typescriptvoidundefined最重要的一個區別: void做爲函數返回值的類型時,能夠用不一樣的類型替換:ui

function doSomething(callback: () => void) {
  // 這個回調會永遠返回undefined
  let c = callback() 
  //c也是undefined類型
}
​
// 該返回返回number類型
function aNumberCallback(): number {
  return 2;
}
​
// 其實這個方法就作到了類型安全。
doSomething(aNumberCallback) 
複製代碼

這種稱爲typescirpt-substitutability(可替換性模式), 若是但願傳遞函數是返回undefined的,必須明確顯式指定回調的簽名是:() => undefinedthis

2.Symbol

Symbol in JavaScript

建立Symbol很是簡單,使用Symbol構造出來的實例具備惟一的值:spa

const ACADEMIC_TITLE = Symbol('title')
const ARTICLE_TITLE = Symbol('title')

if(ACADEMIC_TITLE === ARTICLE_TITLE) {
  // 兩個Symbol永遠不會相同
}
複製代碼

Switch語句中使用Symboldebug

const LEVEL_INFO = Symbol('INFO')
const LEVEL_DEBUG = Symbol('DEBUG')
const LEVEL_WARN = Symbol('WARN')
const LEVEL_ERROR = Symbol('ERROR')

function log(msg, level) {
  switch(level) {
    case LEVEL_WARN: 
      console.warn(msg); break
    case LEVEL_ERROR: 
      console.error(msg); break;
    case LEVEL_DEBUG: 
      console.log(msg); 
      debugger; break;
    case LEVEL_INFO:
      console.log(msg);
  }
}
複製代碼

做爲對象的key

const print = Symbol('print')
const user = {
  name: 'Stefan',
  age: 37,
  [print]: function() {
    console.log(`${this.name} is ${this.age} years old`)
  }
}
複製代碼

這裏只須要注意如下幾點,就是做爲對象的Key時,這個print是不可枚舉的,同時它也是沒法被序列化的。

Symbol in TypeScript

typescript支持symbol,它是類型系統中的基本類型。Symbol自己是全部symbol值的類型。 它也有一個sub-type:稱爲unique symbol, 它能夠聲明一個惟一的類型:

const PROD: unique symbol = Symbol('prodution mode');
const DEV: unique symbol = Symbol('Development mode');

複製代碼

能夠認爲是JS裏的一個名義類型值。爲了獲得它的類型須要用typeof

function showWarning(msg: string, mode: typeof DEV | typeof PROD) {
 // ...
}
複製代碼

Symbols是名義類型和結構類型之間的中間類型。 這是運行時最接近名義類型的類型,但本質上仍然不是名義類型。

運行時枚舉

ts的枚舉是結構類型,不是名義類型。 這表示你不能直接將string值賦值給enum types

enum Colors {
  Red = 'Red',
  Yellow = 'yellow',
  Blue = 'blue',
}
const c1: Colors = Colors.Blue;
// 這樣倒是不符合語法的 Type '"blue"'沒法賦值給'Colors'類型。
const c2: Colors = 'blue'; 
// false
console.log((Moods.Blue === Moods.Blue));
複製代碼

儘管是一樣的值,可是enum裏面,就會變得獨一無二; 他們在TS裏是沒法比較的。

在JS裏的實現能夠使用Symbol來作到:

// All Color symbols
const COLOR_RED: unique symbol = Symbol('RED')
const COLOR_ORANGE: unique symbol = Symbol('ORANGE')
const COLOR_YELLOW: unique symbol = Symbol('YELLOW')
// create enum
const ColorsEnum = {
    COLOR_RED,
    COLOR_ORANGE,
    COLOR_YELLOW,
} as const;
複製代碼

typescriptswitch語句用法

function getHexValue(color) {
  switch(color) {
    case Colors.COLOR_RED: return '#ff0000'
    //...
  }
}
複製代碼

編譯機時以及運行時的類型安全

  1. 定義全部的symbol鍵值是unique symbol時,全部的賦值都不能改變。
  2. 經過將enum對象用const聲明,TypeScript就會只容許傳入的定義好的Symbol類型。
// 索引類型
type ValuesWithKeys<T, K extends keyof T> = T[K];
type Values<T> = ValuesWithKeys<T, keyof T>;
複製代碼

解釋

index types 索引類型:爲了從對象中選取屬性的子集,編譯器可以檢查使用了動態屬性名即索引類型。

// 如下這種索引類型就是爲了提示編譯器去檢查: 傳入的參數names中是否爲o類型的一個屬性。
function pluck<T, K extends keyof T>(o: T, names: k[]): T[K][] {
    return names.map(n => o[n]);
}
// 索引類型能夠做爲函數的類型;, T[K]表示了必須是T類型中的屬性。

複製代碼

而後利用索引類型,能夠在聲明函數時,將color類型縮窄至只容許Colors類型,而且是Symbol的鍵和值,而不是隻有Symbol自己的值。

const ColorEnum = {
  [COLOR_RED]: COLOR_RED,
  [COLOR_YELLOW]: COLOR_YELLOW,
  [COLOR_ORANGE]: COLOR_ORANGE,
  [COLOR_GREEN]: COLOR_GREEN,
  [COLOR_BLUE]: COLOR_BLUE,
  [COLOR_INDIGO]: COLOR_INDIGO,
  [COLOR_VIOLET]: COLOR_VIOLET,
}

function getHexValue(color: Values<typeof Colors>) {
  switch(color) {
    case COLOR_RED:
      // super fine, is in our type
    case Colors.COLOR_BLUE:
      // also super fine, is in our type
      break;
    case COLOR_BLACK: 
      // what? What is this??? TypeScript errors 💥
      break;
  }
}
複製代碼

這樣就作到了不只在編譯時利用了TypeScriptunique symbol來保證類型安全性,同時在運行時也能夠經過JavaScriptSymbol獨一無二的特性實現類型安全。

Reference

symbol-in-typescript-and-javascript

void-in-javascript-and-typescript

相關文章
相關標籤/搜索