Limbo
Limbo

Reputation: 2290

Defeating the Array<T> and IObservableArray<T> incompatibility

Assume I have some property in my store that is being array of some type MyType and decorated with observable:

class MyStore {
    @observable array
}

I know that in normal world this should be an Array<MyType>. But when I'm declaring it in this way,

class MyStore {
    @observable array: Array<MyType>
}

then I lose the method .remove(item: MyType). On the other hand, if I'm declaring it with IObservableArray<MyType>,

class MyStore {
    @observable array: IObservableArray<MyType>
}

then I lose a possibility to legally assign an Array<MyType> values to this prop (under legally I mean assigning without construction ... as IObservableArray<MyType> - disadvantages of this method are too obvious: a lot of unnecessary code, the type should be imported whenever it used, etc.)

I also tried to use union and intersection types:

  1. Intersection (Array<MyType> & IObservableArray<MyType>) produces an error during assigning an Array<MyType> value to this prop: Property 'spliceWithArray' is missing in type MyType.
  2. Union (Array<MyType> | IObservableArray<MyType>) still causes lost of method .remove(item: MyType).

Am I missing or misunderstanding something? Is there any legal way to defeat it? Thank you all in advance!


By the way, the mobx version I'm using is 4, because it is necessary for me to support old iPads, unfortunately

Upvotes: 2

Views: 1528

Answers (2)

Shevchenko Viktor
Shevchenko Viktor

Reputation: 5396

You need to use the next syntax, this won't generate any TS errors. And you can safely use methods of both Array and IObservableArray

class MyStore {
  readonly array = observable<MyType>([])

  @action
  getItems() {
    api.getItems<MyType[]>().then((response) => this.array.replace(response.data));
  }

  @action
  removeItem(item: MyType) {
    this.array.remove(item);
  }
}

readonly - as you are mostly not intended to do an assignment for array property, like this.array = itemsFromServer

replace, remove - methods of IObservableArray

Upvotes: 2

rotemx
rotemx

Reputation: 954

How about creating your own tailored type, extending Array class and manually implementing the IObservableArray methods as optional?

Since you are locked to [email protected], there's no problem with the maintaining the interface as the version advances.

it's not ideal but it's the only way I can think of that will allow you to assign Array and also use methods like .remove().

I call the type MyExtendedObservableArray:

import {IArrayChange, IArraySplice, IArrayWillChange, IArrayWillSplice, IInterceptor, IObservableArray, Lambda, observable} from "mobx";

interface MyExtendedObservableArray<T> extends Array<T>{
    spliceWithArray?(index: number, deleteCount?: number, newItems?: T[]): T[];
    observe?(listener: (changeData: IArrayChange<T> | IArraySplice<T>) => void, fireImmediately?: boolean): Lambda;
    intercept?(handler: IInterceptor<IArrayWillChange<T> | IArrayWillSplice<T>>): Lambda;
    clear?(): T[];
    peek?(): T[];
    replace?(newItems: T[]): T[];
    find?(predicate: (item: T, index: number, array: IObservableArray<T>) => boolean, thisArg?: any, fromIndex?: number): T | undefined;
    findIndex?(predicate: (item: T, index: number, array: IObservableArray<T>) => boolean, thisArg?: any, fromIndex?: number): number;
    remove?(value: T): boolean;
    move?(fromIndex: number, toIndex: number): void;
    toJS?(): T[];
    toJSON?(): T[];
}

class MyType{}
let myType = new MyType();

class MyStore {
    @observable array: MyExtendedObservableArray<MyType> = [myType] 
    // no TSC compilation error
}

Upvotes: 1

Related Questions