على - شرح javascript




كيفية تمديد مصفوفة جافا سكريبت الأصلية في TypeScript (7)

هل هناك أي طريقة لترث فئة من وظيفة JS الأصلية.

ل (على سبيل المثال) لدي وظيفة شبيبة مثل هذا في بلدي شبيبة.

function Xarray()
{
    Array.apply(this, arguments);
  //some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();

حاولت تحويله إلى Typescript لكنني فشلت !!

export class Xarray implements Array {

}

يسألني compailer تعريف كافة الخاصية Array Interface ، أعرف إذا كان بحاجة إلى هذا Xarray.prototype = new Array(); لا بد لي من تمديد صفيف في TS ، كيفية مدى كائن JS الأصلي في TS؟


أثناء البحث عن هذا ، صادفت مشاركة بن نضال الممتازة حول توسيع مصفوفات جافا سكريبت أثناء الحفاظ على وظائف الترميز السكاني الأصلية . بعد بعض الارتباك الأولي حول كيفية تحويل هذا بنجاح إلى TypeScript ، قمت بإنشاء فئة مجمعة بالكامل يمكن تصنيفها تحت فئة فرعية.

يمكن أن تفعل كل شيء علبة صفيف ، بما في ذلك الفهرسة بواسطة الأقواس ، واستخدامها في إنشاءات حلقة (ل ، في حين ، forEach) ، والخرائط ، إلخ.

النقاط الرئيسية للتنفيذ هي

  1. إنشاء صفيف في المنشئ ، إضافة الأساليب إلى الصفيف وإرجاع ذلك من المنشئ
  2. نسخ تعريفات وهمية من أساليب مصفوفة لتمرير implements Array بت implements Array

مثال على الاستخدام:

  var foo = new Foo({id : 1})
  var c = new Collection();

  c.add(foo)
  c.length === 1;    // => true

  foo === c[0];      // => true
  foo === c.find(1); // => true

لقد جعلتها متاحة كنواة ، كاملة مع اختبارات ومثال تنفيذ فئة فرعية ، ولكنني أقدم المصدر الكامل هنا:

/*
 * Utility "class" extending Array with lookup functions
 *
 * Typescript conversion of Ben Nadel's Collection class.
 * https://gist.github.com/fatso83/3773d4cb5f39128b3732
 *
 * @author Carl-Erik Kopseng
 * @author Ben Nadel (javascript original)
 */

export interface Identifiable {
    getId : () => any;
}

export class Collection<T extends Identifiable> implements Array<T> {

    constructor(...initialItems:any[]) {
        var collection = Object.create(Array.prototype);

        Collection.init(collection, initialItems, Collection.prototype);

        return collection;
    }

    static init(collection, initialItems:any[], prototype) {
        Object.getOwnPropertyNames(prototype)
            .forEach((prop) => {
                if (prop === 'constructor') return;

                Object.defineProperty(collection, prop, { value: prototype[prop] })
            });

        // If we don't redefine the property, the length property is suddenly enumerable!
        // Failing to do this, this would fail: Object.keys([]) === Object.keys(new Collection() )
        Object.defineProperty(collection, 'length', {
            value: collection.length,
            writable: true,
            enumerable: false
        });

        var itemsToPush = initialItems;
        if (Array.isArray(initialItems[0]) && initialItems.length === 1) {
            itemsToPush = initialItems[0];
        }
        Array.prototype.push.apply(collection, itemsToPush);

        return collection;
    }

    // Find an element by checking each element's getId() method
    public find(id:any):T;

    // Find an element using a lookup function that
    // returns true when given the right element
    public find(lookupFn:(e:T) => boolean):T ;

    find(x:any) {
        var res, comparitor;

        if (typeof x === 'function') {
            comparitor = x;
        } else {
            comparitor = (e) => {
                return e.getId() === x;
            }
        }

        res = [].filter.call(this, comparitor);

        if (res.length) return res[0];
        else return null;
    }

    // Add an element
    add(value:T);

    // Adds all ements in the array (flattens it)
    add(arr:T[]);

    add(arr:Collection<T>);

    add(value) {

        // Check to see if the item is an array or a subtype thereof
        if (value instanceof Array) {

            // Add each sub-item using default push() method.
            Array.prototype.push.apply(this, value);

        } else {

            // Use the default push() method.
            Array.prototype.push.call(this, value);

        }

        // Return this object reference for method chaining.
        return this;

    }

    remove(elem:T):boolean;

    remove(lookupFn:(e:T) => boolean):boolean ;

    remove(x:any):boolean {
        return !!this._remove(x);
    }

    /**
     * @return the removed element if found, else null
     */
    _remove(x:any):T {
        var arr = this;
        var index = -1;

        if (typeof x === 'function') {

            for (var i = 0, len = arr.length; i < len; i++) {
                if (x(this[i])) {
                    index = i;
                    break;
                }
            }

        } else {
            index = arr.indexOf(x);
        }

        if (index === -1) {
            return null;
        }
        else {
            var res = arr.splice(index, 1);
            return res.length ? res[0] : null;
        }
    }


    // dummy declarations
    // "massaged" the Array interface definitions in lib.d.ts to fit here
    toString:()=> string;
    toLocaleString:()=> string;
    concat:<U extends T[]>(...items:U[])=> T[];
    join:(separator?:string)=> string;
    pop:()=> T;
    push:(...items:T[])=> number;
    reverse:()=> T[];
    shift:()=> T;
    slice:(start?:number, end?:number)=> T[];
    sort:(compareFn?:(a:T, b:T) => number)=> T[];
    splice:(start?:number, deleteCount?:number, ...items:T[])=> T[];
    unshift:(...items:T[])=> number;
    indexOf:(searchElement:T, fromIndex?:number)=> number;
    lastIndexOf:(searchElement:T, fromIndex?:number)=> number;
    every:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
    some:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
    forEach:(callbackfn:(value:T, index:number, array:T[]) => void, thisArg?:any)=> void;
    map:<U>(callbackfn:(value:T, index:number, array:T[]) => U, thisArg?:any)=> U[];
    filter:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> T[];
    reduce:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
    reduceRight:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
    length:number;
[n: number]: T;
}

وبالطبع ، لا توجد حاجة إلى البتات القابلة Identifiable ، ولكن طرق remove والإزالة لا حاجة إليها ، ولكنني أقوم بتزويدها بأشياء أقل كمثال كامل هو صبي أكثر قابلية للاستخدام من مجموعة مكشوفة بدون أي أساليب خاصة به.


إذا كان لديك بالفعل تطبيق Xarray ، فأنا لا أرى نقطة في إعادة تصميمه في typescript ، والتي في النهاية سوف ترجع إلى JavaScript.

لكنني أرى النقطة في القدرة على استخدام Xarray في TypeScript.

من أجل تحقيق ذلك ، تحتاج ببساطة إلى واجهة ل Xarray الخاص بك. لا تحتاج حتى إلى تنفيذ ملموس لواجهةك نظرًا لأن تطبيق js الحالي سيعمل كواحد.

interface Xarray{
    apply(...arguments : any[]) : void;
    //some stuff for insert, add and ...
}
declare var Xarray: {
   new (...items: any[]): Xarray;
   (...items: any[]): Xarray;
   prototype: Array; // This should expose all the Array stuff from ECMAScript 
}

بعد القيام بذلك ، يجب أن تكون قادرًا على استخدام نوعك المعرّف المخصص من خلال المتغير المُعلن بدون تطبيقه فعليًا في TypeScript.

var xArr = new Xarray();
xArr.apply("blah", "hehe", "LOL");

قد تبحث عن مرجع هنا لمعرفة كيفية كتابتها ECMAScript Array API : http://typescript.codeplex.com/SourceControl/changeset/view/2bee84410e02#bin/lib.d.ts


بدءًا من TypeScript 1.6 ، يمكنك تمديد نوع الصفيف ، راجع ما هو الجديد في TypeScript

إليك مثال على ذلك:

class MyNewArray<T> extends Array<T> {
    getFirst() {
        return this[0];
    }
}

var myArray = new MyNewArray<string>();
myArray.push("First Element");
console.log(myArray.getFirst()); // "First Element"

إذا كنت تنبعث منها ES5 أو أدناه ، فاستخدم الرمز التالي:

class MyNewArray<T> extends Array<T> {
    constructor(...items: T[]) {
        super(...items);
        Object.setPrototypeOf(this, MyNewArray.prototype);
    }

    getFirst() {
        return this[0];
    }
}

اقرأ المزيد عن سبب ضرورة ذلك here .


بهدف التغلب على مشكلة تمديد فئة الصفيف الأصلي ، استفدت من الديكور.

function extendArray(constructor: Function) {
    Object.getOwnPropertyNames(constructor.prototype)
        .filter(name => name !== 'constructor')
.forEach(name => {
    const attributes = Object.getOwnPropertyDescriptor(constructor.prototype, name);
    Object.defineProperty(Array.prototype, name, attributes);
  });
}

@extendArray
export class Collection<T> extends Array<T> {
  constructor(...args: T[]) {
    super(...args);
  }
  // my appended methods
}

راجع للشغل يمكن جعل هذا الديكور أكثر عامة (للفئات المحلية الأخرى) إذا كنت تستخدم مصنع ديكور.


لا أعتقد أن هناك طريقة لترث الواجهات الحالية مثل Array ،

export class Xarray implements Array {

}

يجب عليك إنشاء دالة وترثها بنموذجها الأولي. يقبل typescript أيضاً ما يشبه javascript.

function Xarray(...args: any[]): void; // required in TS 0.9.5
function Xarray()
{
    Array.apply(this, arguments);
   // some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();

استكمال: وناقش هذا واحد بشكل جيد وقدمت الحل الأفضل لهذا في jqfaq.com .

//a dummy class it to inherite array.
class XArray {
    constructor() {
        Array.apply(this, arguments);   
        return new Array();
    }
    // we need this, or TS will show an error,
    //XArray["prototype"] = new Array(); will replace with native js arrray function
    pop(): any { return "" };
    push(val): number { return 0; };
    length: number;
}
//Adding Arrray to XArray prototype chain.
XArray["prototype"] = new Array();

//our Class
class YArray extends XArray {
///Some stuff
}

var arr = new YArray();
//we can use the array prop here.
arr.push("one");
arr.push("two");

document.writeln("First Elemet in array : " + arr[0]);
document.writeln("</br>Array Lenght : " + arr.length);

نأمل أن هذا قد يساعدك!!!


لا أعرف كيف أن هذا أمر مفروغ منه ، لكنني على سبيل المثال أحتاج إلى إنشاء مجموعة من BulletTypes حتى أتمكن من التنقل من خلالها. ما فعلته هو ما يلي:

interface BulletTypesArray extends Array<BulletType> {
    DefaultBullet?: Bullet; 
}

var BulletTypes: BulletTypesArray = [ GreenBullet, RedBullet ];
BulletTypes.DefaultBullet = GreenBullet;

من الواضح أنه يمكنك أيضًا إنشاء واجهة عامة ، مثل interface SuperArray<T> extends Array<T> .


نعم من الممكن تمديد كائن JS أصلي في TS ، ومع ذلك هناك مشكلة في توسيع أنواع المضمنة (تلك المدرجة في lib.d.ts) مثل Array. قراءة هذه المشاركة للحل: http://typescript.codeplex.com/workitem/4

لذلك ، يمكن تحديد واجهة نوع والتي تمد كائنًا أصليًا في مرحلة لاحقة بالطريقة التالية:

/// <reference path="lib.d.ts"/>
interface Array {
    sort: (input: Array) => Array;
}

باستخدام مثال ملموس ، يمكنك فرز بعض العناصر في صفيف والتي تحدد وظيفة الفرز في واجهة ويقوم بتطبيقها في وقت لاحق على كائن.

class Math implements Array {
    sort : (x: Array) => Array {
          // sorting the array
    }
}
var x = new Math();
x.sort([2,3,32,3]);




typescript