Open jits opened 1 month ago
The error goes away if you just have
const _Store = signalStore(
{ providedIn: 'root' },
withState(initialState)
);
initialState
has already a declared type, so no need to provide the type a second time. Not sure if that helps in the long run, though.
Thanks @rainerhahnekamp! That works for now (as a workaround) ππ½
@markostanimirovic, if that's the right approach for now, should we leave a note in the docs?
@rainerhahnekamp β I spoke too soon β unfortunately it still fails elsewhere, when using the discriminated union types. I've updated the Stackblitz to show this (https://stackblitz.com/edit/stackblitz-starters-ucjue4?file=src%2Fstore.ts).
Looks like the inferred state type of the store is not picking up the full discriminated union, just the specific type of initialState
.
Union types can be used for state properties, not for the entire state because SignalStore/SignalState generates state signals based on the initial state.
// From:
type State = A | B;
const initialState: State = { /* ... */ };
// To:
type State = { prop: A | B };
const initialState: State = { prop: /* ... */ };
We can add a section about unions to the FAQ page and update the answer for the "Can I define my SignalStore as a class?" question to contain a note that although it's possible to create a class-based SignalStore, using the functional style is recommended.
@markostanimirovic β ahh, that's a shame! I've been using discriminated union types (with extended class) up until (and including) RC2. Is there anything that can be done to support this use case, as I feel it's more akin to modelling state like a state machine, and gives us much better type safety. I'm keen to use this with extended classes so I can add extra properties etc. (as mentioned in https://github.com/ngrx/platform/discussions/4141#discussioncomment-7629034).
A concrete example of where I'm doing this (an AuthStore
):
@jits. nested state with union works. We can take that as workaround
type A = {
status: 'A';
thing: null;
};
type B = {
status: 'B';
thing: string;
};
type NestedState = A | B;
type State = { nested: NestedState };
const initialState: State = { nested: { status: 'A', thing: null } };
const _Store = signalStore(
{ providedIn: 'root' },
withState(initialState),
withMethods((store) => {
return {
updateToB(thing: string): void {
const newState: B = { status: 'B', thing };
patchState(store, { nested: newState });
},
};
})
);
@markostanimirovic β ahh, that's a shame! I've been using discriminated union types (with extended class) up until (and including) RC2. Is there anything that can be done to support this use case, as I feel it's more akin to modelling state like a state machine, and gives us much better type safety. I'm keen to use this with extended classes so I can add extra properties etc. (as mentioned in #4141 (reply in thread)).
A concrete example of where I'm doing this (an
AuthStore
):
Thank you for the detailed explanation @jits!
We'll consider adding a withProps
base feature to add properties to the SignalStore in the next major release. With that feature, I guess class-based stores won't be needed anymore.
In the meantime, I suggest a different approach. Instead of extending a SignalStore, you can create a class that injects it:
const AuthStore = signalStore({ providedIn: 'root' }, /* ... */);
@Injectable({ providedIn: 'root' })
export class AuthFacade {
readonly #store = inject(AuthStore);
// ... expose all public store APIs
// ... add additional properties
}
By the way, discriminated unions for the entire state work with functional SignalStores: https://stackblitz.com/edit/stackblitz-starters-hefo5v?file=src%2Fstore.ts
Thanks for clarifying @markostanimirovic, and for your suggestions.
Sounds like functional SignalStores are the way forward. A future withProps
base feature would be handy, but a wrapper / helper class will work for now.
Congrats to you and the team on the NgRx SignalStore first stable release! π
(Please feel free to close this issue if no further action is needed.)
Which @ngrx/* package(s) are the source of the bug?
signals
Minimal reproduction of the bug/regression with instructions
See https://stackblitz.com/edit/stackblitz-starters-ucjue4?file=src%2Fstore.ts
For posterity, here is the code that causes the typing error:
The typing error occurs on the line
export class Store extends _Store {}
, with the following TypeScript error:Expected behavior
Should compile fine and not give any TypeScript errors.
Versions of NgRx, Angular, Node, affected browser(s) and operating system(s)
NgRx: v18.0.0 Angular: latest v18 TypeScript: v5.5
Other information
No response
I would be willing to submit a PR to fix this issue