simontonsoftware / s-libs

A collection of libraries for any of javascript, rxjs, or angular.
MIT License
43 stars 5 forks source link

[app-state] Make typing support selecting from optional objects #91

Open eric-simonton-sama opened 1 year ago

eric-simonton-sama commented 1 year ago

E.g.

interface State {
  currentUser?: {
    name: string
  };
}

store('currentUser')('name');

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>.

ersimont commented 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.

ersimont commented 1 year ago

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>
  >;
}
ersimont commented 8 months ago

This would also be a good to add .delete() to signal-store, and have it only work for undefined-able things.

ersimont commented 4 months ago

This will be available in signal-store starting in v18.