mobx-state-tree
mobx-state-tree copied to clipboard
Idea: introduce tuple types
like: types.tuple(Car, types.string, types.boolean)
(serialized / stored as array)
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 😅 => 😓.
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()
.
Just my two cents... :)
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
of course it would be a bit more complicated than that for the instance part, but that's the core idea :)
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.
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?
Any update?
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'
}
)
Any updates? Tuple is really useful