Skip to content

Commit

Permalink
chore(store): add initial version of new strongly typed createSelector (
Browse files Browse the repository at this point in the history
  • Loading branch information
markwhitfeld authored Feb 9, 2022
1 parent 5175b98 commit 90fdd58
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 0 deletions.
156 changes: 156 additions & 0 deletions packages/store/src/selectors/create-selector.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { createSelector as createSelectorOrig } from '../utils/selector-utils';
import { SelectorDef, SelectorReturnType } from './selector-types.util';

type SelectorArg = SelectorDef<any>;

type CreationMetadata = Parameters<typeof createSelectorOrig>[2];

// NOTE: This is temporarily named differently to `createSelector`, and
// will be renamed when the types interface is finalised and publically exported.
// Adding this here so that other dependent types can leverage this in the interim.
export function createSelectorX<
S1 extends SelectorArg,
TProjector extends (s1: SelectorReturnType<S1>) => any
>(
selectors: [S1],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<
S1 extends SelectorArg,
S2 extends SelectorArg,
TProjector extends (s1: SelectorReturnType<S1>, s2: SelectorReturnType<S2>) => any
>(
selectors: [S1, S2],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<
S1 extends SelectorArg,
S2 extends SelectorArg,
S3 extends SelectorArg,
TProjector extends (
s1: SelectorReturnType<S1>,
s2: SelectorReturnType<S2>,
s3: SelectorReturnType<S3>
) => any
>(
selectors: [S1, S2, S3],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<
S1 extends SelectorArg,
S2 extends SelectorArg,
S3 extends SelectorArg,
S4 extends SelectorArg,
TProjector extends (
s1: SelectorReturnType<S1>,
s2: SelectorReturnType<S2>,
s3: SelectorReturnType<S3>,
s4: SelectorReturnType<S4>
) => any
>(
selectors: [S1, S2, S3, S4],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<
S1 extends SelectorArg,
S2 extends SelectorArg,
S3 extends SelectorArg,
S4 extends SelectorArg,
S5 extends SelectorArg,
TProjector extends (
s1: SelectorReturnType<S1>,
s2: SelectorReturnType<S2>,
s3: SelectorReturnType<S3>,
s4: SelectorReturnType<S4>,
s5: SelectorReturnType<S5>
) => any
>(
selectors: [S1, S2, S3, S4, S5],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<
S1 extends SelectorArg,
S2 extends SelectorArg,
S3 extends SelectorArg,
S4 extends SelectorArg,
S5 extends SelectorArg,
S6 extends SelectorArg,
TProjector extends (
s1: SelectorReturnType<S1>,
s2: SelectorReturnType<S2>,
s3: SelectorReturnType<S3>,
s4: SelectorReturnType<S4>,
s5: SelectorReturnType<S5>,
s6: SelectorReturnType<S6>
) => any
>(
selectors: [S1, S2, S3, S4, S5, S6],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<
S1 extends SelectorArg,
S2 extends SelectorArg,
S3 extends SelectorArg,
S4 extends SelectorArg,
S5 extends SelectorArg,
S6 extends SelectorArg,
S7 extends SelectorArg,
TProjector extends (
s1: SelectorReturnType<S1>,
s2: SelectorReturnType<S2>,
s3: SelectorReturnType<S3>,
s4: SelectorReturnType<S4>,
s5: SelectorReturnType<S5>,
s6: SelectorReturnType<S6>,
s7: SelectorReturnType<S7>
) => any
>(
selectors: [S1, S2, S3, S4, S5, S6, S7],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<
S1 extends SelectorArg,
S2 extends SelectorArg,
S3 extends SelectorArg,
S4 extends SelectorArg,
S5 extends SelectorArg,
S6 extends SelectorArg,
S7 extends SelectorArg,
S8 extends SelectorArg,
TProjector extends (
s1: SelectorReturnType<S1>,
s2: SelectorReturnType<S2>,
s3: SelectorReturnType<S3>,
s4: SelectorReturnType<S4>,
s5: SelectorReturnType<S5>,
s6: SelectorReturnType<S6>,
s7: SelectorReturnType<S7>,
s8: SelectorReturnType<S8>
) => any
>(
selectors: [S1, S2, S3, S4, S5, S6, S7, S8],
projector: TProjector,
creationMetadata?: Partial<CreationMetadata>
): TProjector;

export function createSelectorX<T extends (...args: any[]) => any>(
selectors: any[],
projector: T,
creationMetadata?: Partial<CreationMetadata>
): T {
return createSelectorOrig<T>(selectors, projector, <CreationMetadata>creationMetadata);
}
28 changes: 28 additions & 0 deletions packages/store/src/selectors/selector-types.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { StateToken } from '@ngxs/store';
import { StateClass } from '@ngxs/store/internals';

export type SelectorFunc<TModel> = (...arg: any[]) => TModel;

export type TypedSelector<TModel> = StateToken<TModel> | SelectorFunc<TModel>;

export type StateSelector = StateClass<any>;

export type SelectorDef<TModel> = StateSelector | TypedSelector<TModel>;

export type SelectorReturnType<T extends SelectorDef<any>> = T extends StateToken<infer R1>
? R1
: T extends SelectorFunc<infer R2>
? R2
: T extends StateClass<any>
? any /* (Block comment to stop prettier breaking the comment below)
// If the state selector is a class then we should infer its return type to `any`, and not to `unknown`.
// Since we'll get an error that `Type 'unknown' is not assignable to type 'AuthStateModel'.`
// The `unknown` type is not overridable when the strict mode is enabled:
// function doSomethingWithArray(array: number[], factory: (x: unknown) => void) {
// array.forEach(factory);
// }
// doSomethingWithArray([1, 2], (x: number) => console.log(x));
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type 'unknown' is not assignable to type 'number'.
*/
: never;

0 comments on commit 90fdd58

Please sign in to comment.