3

I am translating a program from c++ to typescript, and I face a strange behaviour trying to empty an array using the splice technique (How do I empty an array in JavaScript?) to empty an array.

Here is an excerpt of my code in typescript

"use strict"

class UniformGridGeometry<ItemT> extends Array<ItemT> {

    itemType: { new (): ItemT; }

    constructor(itemType: { new (): ItemT; }) {
        //  constructor(itemType: { new (): ItemT; }, uGeomTemplate: UniformGridGeometry<any>) // any : Vorton, Particle, Vec*, Mat*, ...
        //  constructor(itemType: { new (): ItemT; }, uNumElements: number, vMin: Vec3, vMax: Vec3, bPowerOf2: boolean)
        //  constructor(itemType: { new (): ItemT; }, arg?: any, vMin?: Vec3, vMax?: Vec3, bPowerOf2?: boolean) {

        super(); // Array

        this.itemType = itemType;

        // (...)
    }
}


class UniformGrid<ItemT> extends UniformGridGeometry<ItemT> {

    constructor(itemType: { new (): ItemT; }) {
        //  constructor(itemType: { new (): ItemT; }, uGeomTemplate: UniformGridGeometry<any>) // any : Vorton, Particle, Vec*, Mat*, ...
        //  constructor(itemType: { new (): ItemT; }, uNumElements: number, vMin: Vec3, vMax: Vec3, bPowerOf2: boolean)
        //  constructor(itemType: { new (): ItemT; }, arg?: any, vMin?: Vec3, vMax?: Vec3, bPowerOf2?: boolean) {

        super(itemType);

        // (...)

    }
}

class NestedGrid<ItemT> extends Array<UniformGrid<ItemT>> {

    constructor(src?: UniformGrid<ItemT>) {
        super();

        if (src) {
            this.Init(src);
        }
    }

    Init(src: UniformGrid<ItemT>) {

        this.splice(0, this.length) // mUniformGrids.Clear() ;
        console.assert(typeof src === 'object', typeof src);
        // let numUniformGrids = this.PrecomputeNumUniformGrids( src ) ;
        // this.mUniformGrids.Reserve( numUniformGrids ) ;  // Preallocate number of UniformGrids to avoid reallocation during PushBack.

        let uniformGrid = new UniformGrid<ItemT>(src.itemType);
        //   uniformGrid.Decimate( src , 1 ) ;
        //   uniformGrid.Init() ;
        this.push(uniformGrid);

        // (...)
    }
}

function doTests() {

    console.info("Test > NestedGrid ; UniformGrid");

    let mInfluenceTree: NestedGrid<any> = new NestedGrid<any>();   // Influence tree
    let ugSkeleton = new UniformGrid<any>(null);
    mInfluenceTree.Init(ugSkeleton);

    console.log(mInfluenceTree);

    mInfluenceTree.Init(ugSkeleton);

    console.log(mInfluenceTree);
}

doTests();

that generates (ES6 target) the following Javascript :

"use strict";
class UniformGridGeometry extends Array {
    constructor(itemType) {
        super();
        this.itemType = itemType;
    }
}
class UniformGrid extends UniformGridGeometry {
    constructor(itemType) {
        super(itemType);
    }
}
class NestedGrid extends Array {
    constructor(src) {
        super();
        if (src) {
            this.Init(src);
        }
    }
    Init(src) {
        this.splice(0, this.length);
        console.assert(typeof src === 'object', typeof src);
        let uniformGrid = new UniformGrid(src.itemType);
        this.push(uniformGrid);
    }
}
function doTests() {
    console.info("Test > NestedGrid ; UniformGrid");
    let mInfluenceTree = new NestedGrid();
    let ugSkeleton = new UniformGrid(null);
    mInfluenceTree.Init(ugSkeleton);
    console.log(mInfluenceTree);
    mInfluenceTree.Init(ugSkeleton);
    console.log(mInfluenceTree);
}
doTests();

The same code, on firefox or as code snippet works well, but on chromium the assertion fails, the argument 'src' becomes a number (the size of the array in fact) what am I doing wrong ? (the two Init calls simulates the processing in the WebGL loop)

chromium splice failing

Thanks.

Community
  • 1
  • 1

1 Answers1

1

It looks like splice, which creates a new array to return the deleted elements, reuses the class of the element it's called on, hence calling your custom constructor with the desired size.

Here you can fix the problem by using the other way to empty an array: setting its size to 0. Replace

this.splice(0, this.length);

with

this.length = 0;

Another solution might have been to respect the contract of the class you extends as Array's constructor has a different behavior than the one you're implementing in the subclass.

Note that you're in a grey area, regarding specifications. It's probably wiser to avoid extending basic classes like Array.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Thank you for the explanation, I didn't notice this conflict with the "arrayLength" constructor for Array ! The fix works well with chromium, but I will follow your advice so I won't have to extend the Array class at all, I will indeed rather change the design to create a new array, instead of emptying it from within the extended class. – Gwym Hendawyr Jul 06 '16 at 13:06