Open eric-simonton-sama opened 1 year ago
This is straightforward when thinking about .$
or .state()
. But how should it handle .set()
and other mutating functions? If an ancestor could be undefined, those functions will fail.
Typescript does not allow: store.currentUser?.name = 'John'
, so it seems reasonable for AppState not to allow store('currentUser')('name').set('John')
So the question is: in what way should it be forbidden? So far the answer has been to force the user to declare the store to be defined:
store<'currentUser', User>('currentUser')('name').set('John'); // require this
store.currentUser!.name = 'John'; // analogous to this
Another option could be to carry some kind of flag in the typing of Store object to indicate whether they are writable. Maybe a read-only store interface that is returned when a parent store could be undefined.
Potential typing for the read-only part of things:
type GetSlice<T> = <K extends keyof NonNullable<T>>(
attr: K,
) => Store<NonNullable<T>[K] | IfCouldBe<Nil, T, undefined>>;
export interface Store<T> extends GetSlice<T> {
/**
* Select a slice of the store to operate on. For example `store('currentUser')` will return a new `Store` that represents the `currentUser` property.
*/
<K extends keyof NonNullable<T>>(attr: K): Store<
NonNullable<T>[K] | IfCouldBe<Nil, T, undefined>
>;
}
This would also be a good to add .delete()
to signal-store
, and have it only work for undefined-able things.
This will be available in signal-store
starting in v18.
E.g.
Currently that causes a typescript error saying something like "never does not have a property 'name'". Instead make it work, and have the return type be
Store<string | undefined>
.