簡體   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