typescript - TypeScript:扩展原生类会给出不正确的原型
问题描述
问题
在 TypeScript 中,当我这样做时:
class B { ... }
class A extends B { ... }
我可以正常调用原型 A 的方法/获取器/设置器,而不会出错。
但是,当我对原生类做同样的事情时:
class A extends Array { ... }
当我调用原型 A 的方法/获取器/设置器时,它会抛出,因为原型 A 中的属性都是未定义的。
为什么?
例子
鉴于此工作代码:
class Animal {
protected name: string;
protected age: number;
constructor(name: string, age: number){
this.name = name;
}
eat(){
console.log(`${this.name} has eaten.`);
}
sleep(){
console.log(`${this.name} has slept.`);
}
}
class Dog extends Animal {
constructor(...args: [string, number]){
super(...args);
}
bark(){
console.log(`${this.name} has barked.`);
}
}
const dog = new Dog('doggo', 2);
dog.eat();
dog.bark();
它将被转译成:
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 Animal = /** @class */ (function () {
function Animal(name, age) {
this.name = name;
}
Animal.prototype.eat = function () {
console.log(this.name + " has eaten.");
};
Animal.prototype.sleep = function () {
console.log(this.name + " has slept.");
};
return Animal;
}());
var Dog = /** @class */ (function (_super) {
__extends(Dog, _super);
function Dog() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return _super.apply(this, args) || this;
}
Dog.prototype.bark = function () {
console.log(this.name + " has barked.");
};
return Dog;
}(Animal));
var dog = new Dog('doggo', 2);
dog.eat();
dog.bark();
到目前为止,一切都很好。
现在,当我尝试扩展本机类(在本例中为Array
)时:
class List<T> extends Array<T> {
private index: number;
constructor(...items: T[]) {
super(...items);
Object.defineProperty(this, 'index', {
value: 0,
writable: true,
configurable: true
});
}
get current() {
return this[this.index];
}
get max() {
return this.length - 1;
}
next() {
return this.index === this.max
? this.first()
: this[++this.index];
}
prev() {
return this.index === 0
? this.last()
: this[--this.index];
}
first() {
return this[this.index = 0];
}
last() {
return this[this.index = this.max];
}
}
const list = new List(1, 2, 3);
console.log('List:', list);
console.log('Length:', list.length);
console.log('Current value:', list.current);
console.log('Next value:', list.next());
它被转译成以下代码:
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 List = /** @class */ (function (_super) {
__extends(List, _super);
function List() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
var _this = _super.apply(this, items) || this;
Object.defineProperty(_this, 'index', {
value: 0,
writable: true,
configurable: true
});
return _this;
}
Object.defineProperty(List.prototype, "current", {
get: function () {
return this[this.index];
},
enumerable: true,
configurable: true
});
Object.defineProperty(List.prototype, "max", {
get: function () {
return this.length - 1;
},
enumerable: true,
configurable: true
});
List.prototype.next = function () {
return this.index === this.max
? this.first()
: this[++this.index];
};
List.prototype.prev = function () {
return this.index === 0
? this.last()
: this[--this.index];
};
List.prototype.first = function () {
return this[this.index = 0];
};
List.prototype.last = function () {
return this[this.index = this.max];
};
return List;
}(Array));
var list = new List(1, 2, 3);
console.log('List:', list);
console.log('Length:', list.length);
console.log('Current value:', list.current);
console.log('Next value:', list.next());
为什么?
解决方案
我看到你的目标是 ES5。转译到 ESNext 时,您的代码将按预期完美运行。如果您需要以 ES5 为目标并扩展原生类型(例如Array
),则必须在构造函数中设置您自己的类的原型(Object.setPrototypeOf)。
class List<T> extends Array<T> {
constructor(...items: T[]) {
super(...items);
// ...
Object.setPrototypeOf(this, Object.create(List.prototype)); // (Object as any).setPrototypeOf
}
// ...
}
检查 操场示例中的控制台。
还要检查另一个有用的链接,了解如何扩展原生类型,如Array
.
推荐阅读
- firebase - 如何在没有应用程序失败的情况下添加 firebase_messaging 依赖项
- reactjs - 保存扩展名为 .jsx 的文件时,eslint-watch 未运行
- sql - 如果在 group by 中使用子查询会出错:无法对包含聚合或子查询的表达式执行聚合函数
- c++ - 为什么线程中缓冲区的更改不会反映在 mainThread 上的缓冲区中?
- azure - Linux 上的 Azure 应用服务不显示 Web 服务器日志
- swiftui - SwiftUI:如何使用 UserDefaults 持久化 @Published 变量?
- python - Tox 失败,因为 setup.py 找不到 requirements.txt
- javascript - Reactjs:使用辅助类和函数从 API 获取数据
- sql - 如何找到在任何一年下订单然后一年后没有下订单的用户
- reactjs - 使用反应将类别添加到新任务