enum 与 const enum

在编译后enum(枚举)的产物是一个对象,而const enum(常值枚举)的产物会直接内联在使用处。

  • enum编译表现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 编译前
enum MyEnum {
Foo = 'Foo',
Bar = 'Bar',
}

console.log(MyEnum.Foo)

// 编译后
var MyEnum
;(function (MyEnum) {
MyEnum['Foo'] = 'Foo'
MyEnum['Bar'] = 'Bar'
})(MyEnum || (MyEnum = {}))

console.log(MyEnum.Foo)
  • const enum编译表现
1
2
3
4
5
6
7
8
9
10
// 编译前
const enum MyEnum {
Foo = 'Foo',
Bar = 'Bar',
}

console.log(MyEnum.Foo)

// 编译后
console.log('Foo' /* MyEnum.Foo */)

正确的使用常值枚举可减少代码打包体积,提升执行效率并减少运行内存

但是常值枚举相比于普通枚举也有一定局限性,如不能执行对象相关的操作,不能动态计算成员值等

1
2
3
4
5
6
7
enum MyEnum {
Foo = 'Foo',
Bar = 'Bar',
}

const keys = Object.keys(MyEnum)
console.log(keys) // [ 'Foo', 'Bar' ]

添加新属性

加入想给对象添加一个新属性并保持类型正确,那么可以在global.d.ts中添加全局类型定义声明

1
2
3
4
5
6
7
8
9
10
// global.d.ts
export {}
declare global {
interface Window {
openModal: any
}
}

// 使用
window.openModal()

bindThis 装饰器

该装饰器通常用于在传递或结构中保持正确的this指向,使用场景如

  • react类组件向子组件传递类内方法(会丢失该方法的 this 指向)
  • 使用解构的方法直接创建类并获得方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const bindThis = <T extends Function>(
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> => {
const value = descriptor.value
return {
configurable: true,
get() {
const bound = value?.bind(this)
return bound
},
}
}

class MyClass {
tag: string
constructor() {
this.tag = 'my-class'
}

@bindThis
fn() {
console.log(this.tag)
}
}

const { fn } = new MyClass()

fn() // my-class

限制实现 static 方法

由于 static 方法在类被声明时调用,这与 abstract 的设计思想冲突

TS1243: static modifier cannot be used with abstract modifier.

然而在某些情境下,实现通用的 static 方法完成某些设计模式(工厂或 DI)是简洁且优雅的

可以通过类装饰器来收集判断是否存在特定的 static 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
abstract class MyAbstract {
abstract execute(): void

myStatic?(): void
}

const hasStatic = (target: any) => {
if (!target.hasOwnProperty('myStatic')) {
throw new Error()
}

return target
}

@hasStatic // error
class MyClass1 extends MyAbstract {
constructor() {
super()
}

execute() {}
}

@hasStatic // no error
class MyClass2 extends MyAbstract {
constructor() {
super()
}

static myStatic() {}

execute() {}
}