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

相关文章
相关标签/搜索