4

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

我可以使用自定义名称创建集合,效果很好,但我不能使用我的自定义方法。如果我使用 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'));
4

3 回答 3

3

更新的答案

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

在 ES2015 中,返回对象的构造函数隐式地将 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-b​​uiltin-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'));

但是,如果我们将targetin设置tsconfig.json为等于或大于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。

于 2020-07-20T08:36:18.617 回答
0

我可以看到你错过了一些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'));

不会产生任何错误

于 2020-07-20T08:27:04.153 回答
0

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

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

于 2020-07-20T08:29:31.830 回答