使用Typescript改造Terser - 3 不是所有的new都能instanceof

发布于: 2020-05-23作者: 鱼肚最后更新: 2020-09-09

不是所有的new都能instanceof

问题

JS里面有个 instanceof 方法,可以用来判断一个对象是否是某个类的实例。

1
2
3
4
5
6
7
8
9
10
class A {}
class B {}

var a = new A()

a instanceof A
// => true

a instanceof B
// => false

但是在Typescript之中,这个并不是一直成立的。

假设有这样一段JS代码:

1
2
3
4
5
6
7
8
9
10
11
class MyError extends Error {
    constructor () {
        super()
        this.someAttr = 'hello'
    }
}

const err = new MyError()

console.log('err instanceof MyError: ', (err instanceof MyError))
// err instanceof MyError:  true

但是在将其转换成TS之后:

1
2
3
4
5
6
7
8
9
10
11
class MyError extends Error {
    filename: string
    constructor () {
        super()
        this.filename = 'hello'
    }
}

const err = new MyError()

console.log('err instanceof MyError: ', (err instanceof MyError))

使用ts-node(with typescript >= 2)运行上述代码,却会得到下面的结果:

err instanceof MyError: false

更神奇的是:如果加上以下的一段 tsconfig.json

1
2
3
4
5
{
  "compilerOptions": {
    "target": "es6"
  }
}

ts-node运行的结果就又变成// err instanceof MyError: true

解决方案

一般搜索之后,在Typescript的Breaking Changes 中找到了一段说明。

里面有提到相应的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyError extends Error {
    filename: string
    constructor () {
        super()
        this.filename = 'hello'
        Object.setPrototypeOf(this, MyError.prototype)
    }
}

const err = new MyError()

console.log('err instanceof MyError: ', (err instanceof MyError))
// err instanceof MyError:  true

或者像上面提到的,将tsconfig.json中的target改成es6,也可解决这个问题。

相关原理

刚刚发的Breaking Changes 中有提到:

As part of substituting the value of this with the value returned by a super(...) call, subclassing Error, Array, and others may no longer work as expected. This is due to the fact that constructor functions for Error, Array, and the like use ECMAScript 6's new.target to adjust the prototype chain; however, there is no way to ensure a value for new.target when invoking a constructor in ECMAScript 5.

关注我:
分享文章:

0条评论