不是所有的new都能instanceof
问题
JS里面有个 instanceof 方法,可以用来判断一个对象是否是某个类的实例。
1class A {} 2class B {} 3 4var a = new A() 5 6a instanceof A 7// => true 8 9a instanceof B 10// => false
但是在Typescript之中,这个并不是一直成立的。
假设有这样一段JS代码:
1class MyError extends Error { 2 constructor () { 3 super() 4 this.someAttr = 'hello' 5 } 6} 7 8const err = new MyError() 9 10console.log('err instanceof MyError: ', (err instanceof MyError)) 11// err instanceof MyError: true
但是在将其转换成TS之后:
1class MyError extends Error { 2 filename: string 3 constructor () { 4 super() 5 this.filename = 'hello' 6 } 7} 8 9const err = new MyError() 10 11console.log('err instanceof MyError: ', (err instanceof MyError))
使用ts-node(with typescript >= 2)
运行上述代码,却会得到下面的结果:
err instanceof MyError: false
更神奇的是:如果加上以下的一段 tsconfig.json
1{ 2 "compilerOptions": { 3 "target": "es6" 4 } 5}
ts-node
运行的结果就又变成// err instanceof MyError: true
解决方案
一般搜索之后,在Typescript的Breaking Changes 中找到了一段说明。
里面有提到相应的解决方案:
1class MyError extends Error { 2 filename: string 3 constructor () { 4 super() 5 this.filename = 'hello' 6 Object.setPrototypeOf(this, MyError.prototype) 7 } 8} 9 10const err = new MyError() 11 12console.log('err instanceof MyError: ', (err instanceof MyError)) 13// err instanceof MyError: true
或者像上面提到的,将tsconfig.json
中的target改成es6
,也可解决这个问题。
相关原理
刚刚发的Breaking Changes 中有提到:
As part of substituting the value of
this
with the value returned by asuper(...)
call, subclassingError
,Array
, and others may no longer work as expected. This is due to the fact that constructor functions forError
,Array
, and the like use ECMAScript 6'snew.target
to adjust the prototype chain; however, there is no way to ensure a value fornew.target
when invoking a constructor in ECMAScript 5.