1. Void
void in JavaScript
let i = void 2; // i === undefined
Why 为什么需要void?
因为早期js里,人们可以重写undefined,然后赋予真正的值给它,而void总是返回真正的undefined。
- 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总是可以执行它后面的表达式。
- 使用void执行函数会同时执行传入的回调。
function middleware(nextcb) {
if(conditionApplies()){
return void nextcb();
}
}
- void最重要的用例: 就是保证函数永远返回
undefined,因为返回值是动态地,返回值可能会根据方法内的逻辑返回,但是void却可以100%保证返回undefined。
button.onclick = () => void doSomething();
void in TypeScript
定义
- void在ts里面则是一种基本类型。
- void在ts也是
undefined的子类型。
用法
- 在ts中,如果函数没写返回类型,或者没有return语句,自动返回
undefined
function () {
console.log('xx')
}
void可以作为类型声明中的参数。
declare function iTakeNoParameters(x: void): void
此时只有
undefined可以作为参数传入
iTakeNoParameters(undefined) //
iTakeNoParameters(void 2) //
void的最大特点
在Typescript中void和undefined最重要的一个区别: void作为函数返回值的类型时,可以用不同的类型替换:
function doSomething(callback: () => void) {
// 这个回调会永远返回undefined
let c = callback()
//c也是undefined类型
}
// 该返回返回number类型
function aNumberCallback(): number {
return 2;
}
// 其实这个方法就做到了类型安全。
doSomething(aNumberCallback)
这种称为typescirpt-substitutability(可替换性模式),
如果希望传递函数是返回undefined的,必须明确显式指定回调的签名是:() => undefined
2.Symbol
Symbol in JavaScript
创建Symbol非常简单,使用Symbol构造出来的实例具有唯一的值:
const ACADEMIC_TITLE = Symbol('title')
const ARTICLE_TITLE = Symbol('title')
if(ACADEMIC_TITLE === ARTICLE_TITLE) {
// 两个Symbol永远不会相同
}
Switch语句中使用Symbol
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;
typescript的switch语句用法
function getHexValue(color) {
switch(color) {
case Colors.COLOR_RED: return '#ff0000'
//...
}
}
编译机时以及运行时的类型安全
- 定义所有的symbol键值是
unique symbol时,所有的赋值都不能改变。 - 通过将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;
}
}
这样就做到了不仅在编译时利用了TypeScript的unique symbol来保证类型安全性,同时在运行时也可以通过JavaScript中Symbol独一无二的特性实现类型安全。