-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Type inference for createSelector is broken since changing to variadic tuple types #3268
Comments
Contextual inference for the projector's return value based on its arguments fails when it's variadic. Not sure of the solution yet. Here's a reproduction that shows the inference works when explicitly declaring a creator function which only takes in "one slice" of state vs one that takes in a variadic number of slices: playground type Selector<T, V> = (state: T) => V;
declare function contextualInferenceOneSlice<T, U>(...args: [getSlice: () => T, projector: (c: T) => U]): Selector<unknown, U>;
declare function contextualInferenceVariadicSlices<Slices extends unknown[], U>(...args: [...getSlices: { [i in keyof Slices]: Selector<unknown, Slices[i]> }, projector: (...args: Slices) => U]): Selector<unknown, U>;
const state: { first: string } = {first: 'state'};
const getData = <T>(d: T) => d;
const returnState = () => state;
const oneSlice = contextualInferenceOneSlice(returnState, getData);
const test1 = oneSlice(state).first;
const variadicSlices = contextualInferenceVariadicSlices(returnState, getData);
const test2 = variadicSlices(state).first; // error |
Note that the inference works fine when providing the function plainly without generics: const goodSelector = createSelector(returnState, d => d);
const test1 = goodSelector(state).first; // works
const badSelector = createSelector(returnState, <T>(d: T) => d);
const test2 = badSelector(state).first; // error |
Finding the mechanism by which |
Filed an issue in TS: microsoft/TypeScript#47226 Not sure if the issue by design. But we may need to implement overload signatures again to patch this regression, as recommended in https://stackoverflow.com/a/70469205/11087018 |
For anyone running into this issue: |
This is actually a madness of a new type definition. With a mix of a new
|
We're sorry to hear that @AmebaBrain - how can we improve this? If it's related to To be clear, the |
Hey there, we just ran into this issue when upgrading our application to v13, given that the problem doesn't seem possible to solve with the version of typescript angular v13 is currently on, I guess there's no other option but to revert the variadic tuple types? Happy to assist with this, would it be possible to land it in a 13.x version of ngrx or would it need to wait for v14? |
@david-shortman we could provide both options, right? Keep the tuple for most of the selectors (and allow X amount of child selectors), but re-add the removed selectors as a fallback option? |
Yeah. I was hoping to see more activity on microsoft/TypeScript#47226, but it's only been marked as "Needs Investigation". I'll author a PR. |
However, I'm not sure transitioning back is worth it. Here's the two broken scenarios I see, and why they have a small impact:
// Say we have a state like so
const state: { a: string, b: number } = { a: 'a', b: 2 };
// We could have a reusable projector like this, but it's kinda useless
const identity = <T>(t: T) => t;
// This type of reusable projector seems more useful, and has a constraint on the generic argument
const getA = <T extends { a: string }>(t: T) => t.a;
const getState = () => state;
const oneSlice = createSelector(getState, identity);
const test1: string = oneSlice(state).a; // this has an error because inference fails and oneSlice is determined to operate on unknown instead of the state
const variadicSlices = createSelector(getState, getA);
const test2 = variadicSlices(state); // this works just fine, test2 is inferred to be a string
|
This bug actually causes 528 type errors on our application. Mostly because we have a lot of legacy selectors that followed the very old ngrx style guide that recommended separating We can and will get rid of these usages but given that this breakage wasn't intended, documented or given a proper deprecation lifecycle I think the right course of action would be to transition back at least for a few major versions to give people time to migrate away. |
I believe those errors fall into bucket 2. If we get rid of selectors with props, then those selectors will not be incorrectly inferred as selector with props. Still, since that's not on the horizon that I'm aware of, it sounds like there's enough motivation to put back the explicit overloads. |
Ahh I see, that makes sense now and I see what's happening
Awesome, let me know if there's anything I can do to help! 🙌 |
@david-shortman we did just discuss this, and if you want you can re-add the removed selector types. |
Second what Tim said. We'll cut a release after that PR lands |
Minimal reproduction of the bug/regression with instructions:
As of v13 above code will give a compile error saying: Object is of type 'unknown'.
Expected behavior:
Expected that createSelector would be able to infer the return type for
selector
correctly to{first : string}
Versions of NgRx, Angular, Node, affected browser(s) and operating system(s):
NgRx 13
The text was updated successfully, but these errors were encountered: