Type-Safe Selectors
NGXS v18+ pushes toward stronger type safety for selectors. The key challenge is that state classes do not carry their model type as part of the selector contract — when you pass a state class to @Selector([MyState]) or createSelector([MyState], ...), TypeScript cannot verify the annotated parameter type against the actual state model.
This page shows how to move from less type-safe patterns to fully type-safe ones, and introduces the selector utilities designed for this purpose.
The Problem with State Classes as Selectors
When you write:
@Selector([ZooState])
static getAnimals(state: ZooStateModel) { ... }The ZooStateModel annotation on state is not enforced by TypeScript — you could annotate it as any type and the compiler would not complain. The ZooState class does not carry model type information.
Using a StateToken
A StateToken ties the state name to its model type at the type level:
import { StateToken } from '@ngxs/store';
export const ZOO_STATE_TOKEN = new StateToken<ZooStateModel>('zoo');Using the token as the name in @State and as the selector source makes the model type flow through automatically:
@State({ name: ZOO_STATE_TOKEN, defaults: { animals: [] } })
@Service({ autoProvided: false })
export class ZooState {}
export class ZooSelectors {
// TypeScript now enforces that `state` is ZooStateModel
@Selector([ZOO_STATE_TOKEN])
static getAnimals(state: ZooStateModel) {
return state.animals;
}
}The token can also be passed directly to select() to select the full state model:
Wrapping an Existing State Class
If migrating to StateToken is not immediately possible, you can wrap an existing state class in a typed createSelector call:
This gives downstream selectors a properly-typed input without modifying the state class:
Slicing State with createPropertySelectors
Rather than writing a separate @Selector for every property of a model, createPropertySelectors generates a typed selector for each property automatically:
When passing a StateToken, no explicit type parameter is needed because the token already carries the model type. When passing a bare state class, you must supply the type manually:
The slices can be consumed directly in components:
Or composed into other selectors:
Selecting a Subset with createPickSelector
createPickSelector takes a typed selector and an array of keys, and returns a selector that emits only when one of those picked properties changes. This is useful when a component only cares about part of a larger model:
createPickSelector requires a strongly-typed selector or StateToken. Passing a bare state class loses type information for the picked properties.
Building View Models with createModelSelector
createModelSelector takes a map of selectors and returns a single selector whose output object has the same keys. This is the recommended way to assemble component view models from multiple states:
The output type is fully inferred from the selector map — no annotations required — provided each input selector is itself strongly typed (from a StateToken, createPropertySelectors, or an explicitly-typed @Selector).
Last updated

