Typescript遇到的一个奇妙问题

2018-12-21 23:18

前两天在编写TypeScript(v3.2.2)逻辑时,遇到了一个奇妙的问题阻碍了我一点时间,我用下面的代码来复现并解释一下这个问题。

问题重现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// test.ts
class GameObject {
protected id = 0;
constructor(data) {
this.update(data);
}
update(data) {
this.id = data.id;
}
}

class Role extends GameObject {
name = "";
update(data) {
super.update(data);
this.name = data.name;
}
}

let role = new Role({ id: 1, name: "fox" });
console.log(role.name);

试着猜想一下上面这份代码编译执行之后会打印什么,当然我希望的是打印出fox。但在实际运行之后打印的结果有些出乎我的意料,它打印了’’(空字符串),这不是我想要的结果。我很疑惑,但是从这份代码上似乎看不出有什么问题,就只好从编译之后的JavaScript代码下手,试着找出造成这个问题的答案。

下面是编译之后的JavaScript代码。

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
34
35
36
37
38
39
// test.js
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var GameObject = /** @class */ (function () {
function GameObject(data) {
this.id = 0;
this.update(data);
}
GameObject.prototype.update = function (data) {
this.id = data.id;
};
return GameObject;
}());
var Role = /** @class */ (function (_super) {
__extends(Role, _super);
function Role() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.name = "";
return _this;
}
Role.prototype.update = function (data) {
_super.prototype.update.call(this, data);
this.name = data.name;
};
return Role;
}(GameObject));
var role = new Role({ id: 1, name: "fox" });
console.log(role.name);

于是,我们从Role类的构建方法就能得出问题的答案。在Role的构建方法中首先调用GameObject的构造方法(_super)进行初始化,由于我们的Role类重写了GameObject的update方法,所以我们如愿以偿的初始化了name与id两个成员变量,但是基类构建方法执行完毕之后才开始进行Role的成员初始化,所以导致Role的成员变量初始值覆盖了之前我们在update中改变的变量值而造成了这个问题。

1
2
3
4
5
function Role() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.name = "";
return _this;
}

补充

如果觉得之前那份代码用来举例不是很好的话,也可以看一下这份,它们拥有一样的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class GameObject {
constructor(data) {
this.update(data);
}
abstract update(data);
}

class Role extends GameObject {
name = "";
update(data) {
this.name = data.name;
}
}

let role = new Role({ name: "fox" });
console.log(role.name);

临时解决方式

我们就找到了造成问题的地方,接下来我们试着解决它。

方式1

添加Role类的constructor方法,并在下面做一个判定来检查是否为空后赋初始值。

1
2
3
4
5
6
// test.ts
constructor(data) {
super(data)
if (!this.name)
this.name = "";
}

方式2

添加Role类的constructor方法,但是这样就调用了两次update方法,感觉也并不是很好。

1
2
3
4
5
// test.ts
constructor(data) {
super(data);
this.update(data);
}

当然解决的办法并不只是这两种,还有很多就不一一例举了.

后记

我认为这是TyperScript的一个bug,所以我已经在github提了issue,或许在过一段时间之后微软就会修复这个问题了吧~


标签: typescript,

Creative Commons ©fox 2017 - 2018 | Theme based on fzheng.me &