繁体   English   中英

使用 ES6 类扩展内置数组 - 方法不是 function 错误

[英]Extending Built-in Array With ES6 Classes - Method is not a function error

我正在尝试扩展内置数组 class 并创建基于自定义 collections 的本机数组 class。

我可以使用自定义名称创建集合,效果很好,但我不能使用我的自定义方法。 如果我使用 findAnimal 方法,它给我method is not a function错误。 如果我记录收集并检查原型,我将看不到我的自定义方法。

我意识到我将代码转换为 es5,如果我没有,效果很好,ts 编译器的 es5 降级代码会出现问题。

如果我们使用 babel 将代码降级为 es5,转译后的代码效果很好。 ts 编译器在转译代码时遗漏了一些东西,或者我遗漏了一些重要的配置。

interface Animal {
    name: string;
    weight: number
}

class AnimalCollection extends Array <Animal> {
    constructor(name, ...items) {
        super(...items);

        Object.defineProperty(this, 'name', {
            enumerable: false,
            writable: false,
            value: name
        })
    }

    findAnimal(name): Animal {
        return this.find(a => a.name === name) || null;
    }
}

const animalsArray = [
    {name: 'TD-23', weight: 60},
    {name: 'TD-25', weight: 50},
    {name: 'TXD-26', weight: 120},
    {name: 'TYD-26', weight: 40}
];

const animals = new AnimalCollection('Deers', ...animalsArray)

console.log(animals.findAnimal('TD-23'));
// Uncaught TypeError: animals.findAnimal is not a function

ES5 降级

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 __spreadArrays = (this && this.__spreadArrays) || function () {
    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
    for (var r = Array(s), k = 0, i = 0; i < il; i++)
        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
            r[k] = a[j];
    return r;
};

var AnimalCollection = /** @class */ (function (_super) {
    __extends(AnimalCollection, _super);
    function AnimalCollection(name) {
        var items = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            items[_i - 1] = arguments[_i];
        }
        var _this = _super.apply(this, items) || this;
        Object.defineProperty(_this, 'name', {
            enumerable: false,
            writable: false,
            value: name
        });
        return _this;
    }
    AnimalCollection.prototype.findAnimal = function (name) {
        return this.find(function (a) { return a.name === name; }) || null;
    };
    return AnimalCollection;
}(Array));
var animalsArray = [
    { name: 'TD-23', weight: 60 },
    { name: 'TD-25', weight: 50 },
    { name: 'TXD-26', weight: 120 },
    { name: 'TYD-26', weight: 40 }
];
var animals = new (AnimalCollection.bind.apply(AnimalCollection, __spreadArrays([void 0, 'Deers'], animalsArray)))();
console.log(animals.findAnimal('TD-23'));

更新的答案

为了回答您修改后的问题,TypeScript 不允许您扩展内置函数,如ArrayError等。原因写在这里

在 ES2015 中,返回 object 的构造函数隐式地将 this 的值替换为 super(...) 的任何调用者。 生成的构造函数代码必须捕获 super(...) 的任何潜在返回值并将其替换为 this。

因此,子类化 Error、Array 和其他可能不再按预期工作。 这是因为 Error、Array 等的构造函数使用 ECMAScript 6 的 new.target 来调整原型链; 但是,在 ECMAScript 5 中调用构造函数时,无法确保 new.target 的值。其他低级编译器默认情况下通常具有相同的限制。

所以如果你必须在 ES5 环境下扩展内置的Array ,那么你可以尝试 Babel 来编译你的代码。 但是请注意,它有此处所述的限制。

由于 ES5 中的限制(对于 transform-classes 插件),无法正确子类化诸如 Date、Array、DOM 等内置类。 您可以尝试使用基于 Object.setPrototypeOf 和 Reflect.construct 的 babel-plugin-transform-builtin-extend,但它也有一些限制。

旧答案

虽然代码本身可以完美找到并且在浏览器中也可以正常执行,但我认为您遇到的错误是由于 TypeScript 编译器造成的。

对于源代码

interface Animal {
    name: string;
    weight: number
}

class AnimalCollection extends Array <Animal> {
    constructor(name: string, ...items: Animal[]) {
        super(...items);

        Object.defineProperty(this, 'name', {
            enumerable: false,
            writable: false,
            value: name
        })
    }

    findAnimal(name:string): Animal | null {
        return this.find(a => a.name === name) || null;
    }
}

const animalsArray = [
    {name: 'TD-23', weight: 60},
    {name: 'TD-25', weight: 50},
    {name: 'TXD-26', weight: 120},
    {name: 'TYD-26', weight: 40}
];

const animals = new AnimalCollection('Deers', ...animalsArray)

console.log(animals.findAnimal('TD-23'));

如果编译器目标选项设置为ES5 ,那么它会生成破坏实现的代码 它生成的代码是

"use strict";
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 __spreadArrays = (this && this.__spreadArrays) || function () {
    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
    for (var r = Array(s), k = 0, i = 0; i < il; i++)
        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
            r[k] = a[j];
    return r;
};
var AnimalCollection = /** @class */ (function (_super) {
    __extends(AnimalCollection, _super);
    function AnimalCollection(name) {
        var items = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            items[_i - 1] = arguments[_i];
        }
        var _this = _super.apply(this, items) || this;
        Object.defineProperty(_this, 'name', {
            enumerable: false,
            writable: false,
            value: name
        });
        return _this;
    }
    AnimalCollection.prototype.findAnimal = function (name) {
        return this.find(function (a) { return a.name === name; }) || null;
    };
    return AnimalCollection;
}(Array));
var animalsArray = [
    { name: 'TD-23', weight: 60 },
    { name: 'TD-25', weight: 50 },
    { name: 'TXD-26', weight: 120 },
    { name: 'TYD-26', weight: 40 }
];
var animals = new (AnimalCollection.bind.apply(AnimalCollection, __spreadArrays([void 0, 'Deers'], animalsArray)))();
console.log(animals.findAnimal('TD-23'));

但是,如果我们将tsconfig.json中的target设置为等于或大于ES2015 ,那么它生成的代码

"use strict";
class AnimalCollection extends Array {
    constructor(name, ...items) {
        super(...items);
        Object.defineProperty(this, 'name', {
            enumerable: false,
            writable: false,
            value: name
        });
    }
    findAnimal(name) {
        return this.find(a => a.name === name) || null;
    }
}
const animalsArray = [
    { name: 'TD-23', weight: 60 },
    { name: 'TD-25', weight: 50 },
    { name: 'TXD-26', weight: 120 },
    { name: 'TYD-26', weight: 40 }
];
const animals = new AnimalCollection('Deers', ...animalsArray);
console.log(animals.findAnimal('TD-23'));

这当然有效。 所以我认为 ES5 或更低版本的 TypeScript 编译器存在问题,这会破坏实现。 我尝试使用Babel 进行编译,它适用于 ES5。

我可以看到您缺少一些any ,这是对我有用的代码:

interface Animal {
    name: string;
    weight: number
}

class AnimalCollection extends Array <Animal> {
    constructor(name: string, ...items : Array <Animal>) { // <-- missing types
        super(...items);

        Object.defineProperty(this, 'name', {
            enumerable: false,
            writable: false,
            value: name
        })
    }

    findAnimal(name : any): Animal|null { // <-- missing null
        return this.find(a => a.name === name) || null;
    }
}

const animalsArray = [
    {name: 'TD-23', weight: 60},
    {name: 'TD-25', weight: 50},
    {name: 'TXD-26', weight: 120},
    {name: 'TYD-26', weight: 40}
];

const animals = new AnimalCollection('Deers', ...animalsArray)

console.log(animals.findAnimal('TD-23'));

但是,此代码生成以下 JS:

"use strict";
class AnimalCollection extends Array {
    constructor(name, ...items) {
        super(...items);
        Object.defineProperty(this, 'name', {
            enumerable: false,
            writable: false,
            value: name
        });
    }
    findAnimal(name) {
        return this.find(a => a.name === name) || null;
    }
}
const animalsArray = [
    { name: 'TD-23', weight: 60 },
    { name: 'TD-25', weight: 50 },
    { name: 'TXD-26', weight: 120 },
    { name: 'TYD-26', weight: 40 }
];
const animals = new AnimalCollection('Deers', ...animalsArray);
console.log(animals.findAnimal('TD-23'));

不会产生任何错误

这是您编写 function 的方式导致错误

 interface Animal { name: string; weight: number } class AnimalCollection extends Array <Animal> { constructor(name, ...items) { super(...items); Object.defineProperty(this, 'name', { enumerable: false, writable: false, value: name }) } findAnimal:Animal=(name:String)=> { return this.find(a => a.name === name) || null; } } const animalsArray = [ {name: 'TD-23', weight: 60}, {name: 'TD-25', weight: 50}, {name: 'TXD-26', weight: 120}, {name: 'TYD-26', weight: 40} ]; const animals = new AnimalCollection('Deers', ...animalsArray) console.log(animals.findAnimal('TD-23')); // Uncaught TypeError: animals.findAnimal is not a function

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM