Typescript: Using array notation to get items from class

636 views Asked by At

I have class called Candles and a class called Candle. Candles has a property list that contains an array of Candle.

class Candles
{
  public list: Array<Candle>;
}

class Candle
{
  public date: number;
  public high: number;
  public low: number;
  public open: number;
  public close: number;
  public volume: number;
}

I want list to encapsulate with the Candles class. For example,

const candles = new Candles();
candles[3] === candles.list[3];

I want candles[3] to return candles.list[3]

I also want to use map. Example:

candles.map(function(candle: Candle) {
    console.log(candle.high);
});
4

There are 4 answers

0
Aleksey L. On BEST ANSWER

You can intercept and forward calls to inner list using ES6 Proxy:

class Candles {
  public list: Candle[] = [];

  constructor() {
    return new Proxy(this, {
      get(target, propertyKey) {
        console.log('getting ', propertyKey);
        if (propertyKey in target) {
          console.log('own property');
          return target[propertyKey]
        }

        console.log('forward to inner list');
        const property = target.list[propertyKey];
        if (typeof property === "function") {
          return property.bind(target.list);
        }

        return property;
      }
    });
  }
}

To inform the compiler that Candles can be used as an Array add following definition:

interface Candles extends Array<Candle> { }

And now all the array goodies (e.g. push, foreach, map...) can be applied to Candles:

const candles = new Candles();
candles.push({ low: 1 } as Candle);

console.log(candles[0] === candles.list[0]); //true
candles.forEach(c => console.log(c));

Demo in playground.

0
tony On

What you want there is operator overloading. Typescript does not have support for it.

You can learn more about the reasoning why this is the case in the github issue threads: https://github.com/Microsoft/TypeScript/issues/2319 https://github.com/Microsoft/TypeScript/issues/5407

0
Titian Cernicova-Dragomir On

You can't change the behavior of the indexing operation in Typescript because Javascript does not support this either. What you could do is create a type taht extends the standard array, as described in this question

0
Raza On

Here's what I did:

class Candles extends Array<Candle>
{
  constructor(candles: Array<Candle.Data>) {
    super(...candles.map((candle) => new Candle(candle as Candle.Data)));
    Object.setPrototypeOf(Candles, Object.assign(Array.prototype, Candles.prototype));
  }
}

I got rid of list and just extended Array<Candle>. I also added Object.setPrototypeOf(Candles, Object.assign(Array.prototype, Candles.prototype)); to use the Array methods along with Candles's method