Platform: RFC: Capture the type of a selector's projector function in the MemoizedSelector type

Created on 3 Jun 2019  路  6Comments  路  Source: ngrx/platform


When testing a memoized selector's projector function we default the type of the projector to be any. This makes it difficult to catch type changes of the projector function in unit tests.

I recommend the type of MemoizedSelector be extended to have a third type parameter that captures the type of the projector function:

export interface MemoizedSelector<State, Result, ProjectorFn>
  extends Selector<State, Result> {
  release(): void;
  projector: ProjectorFn;
  setResult: (result?: Result) => void;
}

Describe any alternatives/workarounds you're currently using

Live with the lack of type safety. 馃槃

Other information:

If accepted, I would be willing to submit a PR for this feature

[X] Yes (Assistance is provided if you need help submitting a pull request)
[ ] No

Accepting PRs Store community watch enhancement

Most helpful comment

This looks like a non-breaking change (unless some code erroneously was relying on a different return type).
We could change to the following:

export type DefaultProjectorFn<T> = (...args: any[]) => T;

export interface MemoizedSelector<State, Result, ProjectorFn = DefaultProjectorFn<Result>>
  extends Selector<State, Result> {
  release(): void;
  projector: ProjectorFn;
  setResult: (result?: Result) => void;
}

WDYT?

All 6 comments

Having the projector to be any bothered me as well. Would this be a better solution?

export interface MemoizedSelector<State, Result>
  extends Selector<State, Result> {
  release(): void;
  projector: (...args: any[]) => Result;
  setResult: (result?: Result) => void;
}

I'd rather not add third parameter to the MemoizedSelector (that doesn't have a default value) - it would break the world for me.

I would propose adding a default value of AnyFn for ProjectorFn for version 8, and revisit the breaking change for version 9.

@brandonroberts, @alex-okrushko To follow on, I would say that the _interface_ provides a default value for the new third generic parameter:

export interface MemoizedSelector<State, Result, ProjectorFn = AnyFn>
  extends Selector<State, Result> {
  release(): void;
  projector: ProjectorFn;
  setResult: (result?: Result) => void;
}

but using inference from createSelector you get a more specific type for the third parameter.

This looks like a non-breaking change (unless some code erroneously was relying on a different return type).
We could change to the following:

export type DefaultProjectorFn<T> = (...args: any[]) => T;

export interface MemoizedSelector<State, Result, ProjectorFn = DefaultProjectorFn<Result>>
  extends Selector<State, Result> {
  release(): void;
  projector: ProjectorFn;
  setResult: (result?: Result) => void;
}

WDYT?

馃憤

I will take this one 馃憤

Was this page helpful?
0 / 5 - 0 ratings