mobx-state-tree icon indicating copy to clipboard operation
mobx-state-tree copied to clipboard

Idea: introduce tuple types

Open mweststrate opened this issue 6 years ago • 9 comments

like: types.tuple(Car, types.string, types.boolean) (serialized / stored as array)

mweststrate avatar May 09 '18 13:05 mweststrate

Is the tuple type going to be considered a complex-type (as an array) or a utility-type (as a union)?

It is also interesting to know whether there are any thoughts on arity constraints on the tuple type. The question raises from TypeScript's limitations regarding generics+tuples. Related: TypeScript issue on GitHub and StackOverflow describing a work around).


I first thought of this issue as a [good-first-issue]. But now I'm more like 😅 => 😓.

another-guy avatar Oct 26 '18 19:10 another-guy

After thinking a little bit about it, I realize that there might be a compromise solution if it aligns with your overall vision for the library's API surface.

The types class below is purely for demonstration purposes. The important part is the function signature.

class types {
  static tuple<TTuple extends Array<TItem>, TItem>(initialValue: TTuple): { value: TTuple } {
    return { value: initialValue };
  }
}

The usage will either require an actual tuple (defined as such) instance to be provided as initial value:

const rawTuple: [string, number] = ['my tuple', 42];
const byInference = types.tuple(rawTuple);

or an array value can be provided instead, but the hint should be given for the type to be inferred as a tuple:

const byHint = types.tuple(['my tuple', 42] as [string, number]);

Unfortunately, if the array is not defined as tuple and hint is not given, the type will be inferred as (string|number)[] rather than [string, number], which in my opinion devalues the whole idea of types.tuple().

image

Just my two cents... :)

another-guy avatar Oct 27 '18 08:10 another-guy

type UnboxSnapshotIn<T> = { [P in keyof T]: SnapshotIn<T[P]> };
type UnboxSnapshotOut<T> = { [P in keyof T]: SnapshotOut<T[P]> };
type UnboxInstance<T> = { [P in keyof T]: Instance<T[P]> };
type MSTTuple<T extends any[]> = UnboxInstance<T> & IStateTreeNode<UnboxSnapshotIn<T>, UnboxSnapshotOut<T>> & {
  // bunch of methods kind of exclusive to ObservableArray
}

function typesTuple<T extends IAnyType[]>(...args: T): IComplexType<UnboxSnapshotIn<T>, UnboxSnapshotOut<T>, MSTTuple<T>> {
  return null as any; // just for types testing
}

// this is IComplexType<[string, number], [string, number], MSTTuple<[string, number]>>
const TTupple = typesTuple(types.string, types.number);

// this is [string, number]
const tuple = TTupple.create(['hi', 5])

// tuple[0] = 5; // wrong, must be string

requires TS 3.1 though

xaviergonz avatar Oct 27 '18 09:10 xaviergonz

of course it would be a bit more complicated than that for the instance part, but that's the core idea :)

xaviergonz avatar Oct 27 '18 09:10 xaviergonz

I picked up this issue yesterday. I am toying around with it, based on @xaviergonz and @another-guy previous comments, for now but if people are still interested I can try and formalise it a bit more.

NoFishLikeIan avatar Mar 06 '19 09:03 NoFishLikeIan

I've run into a situation where this would be useful. I want to model a literal array of several model types.

I think this should be possible now with newer TS feature but I'm not experienced enough with TS yet to sort out exactly how. The SO answer linked above has been updated with an example using newer TS features that might work in this case. Has anybody explored this now that TS seems to be more capable?

evelant avatar Jun 03 '20 01:06 evelant

Any update?

yfzhe avatar Jan 12 '21 06:01 yfzhe

const tuple = (...Types) => types.refinement(
  `[${Types.map(type => type.name).join(',')}]`,
  types.array(types.union(...Types)), 
  snapshot => {
    if (snapshot.length !== Types.length) return false
    const invalidValue = snapshot
      .findIndex((value, index) => Types[index].is(value) === false)

    return invalidValue === -1
  },
  value => {
    if (value.length !== Types.length) return 'Invalid number of items in tuple'
    return 'Invalid types in tuple'
  }
)

Yurchishin avatar Mar 11 '21 23:03 Yurchishin

Any updates? Tuple is really useful

yf-hk avatar May 02 '21 13:05 yf-hk