Skip to content
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

Fix PayloadAction inference #138

Merged
merged 2 commits into from
Jul 6, 2019
Merged

Fix PayloadAction inference #138

merged 2 commits into from
Jul 6, 2019

Conversation

tydira
Copy link

@tydira tydira commented Apr 30, 2019

By wrapping the P match in SliceActionCreator<P> with a single-element tuple, it prevents the conditional from being checked distributively, which otherwise has the effect of converting a union into an intersection when used with contra-variant types.

Without this change, the modified test emits the following error:

Argument of type '2' is not assignable to parameter of type 'number & number[]'. Type '2' is not assignable to type 'number[]'.

Argument of type 'number[]' is not assignable to parameter of type 'number & number[]'. Type 'number[]' is not assignable to type 'number'.

My original two comments on this issue are here: #133 (comment) and here: #133 (comment). More information about distributive conditionals can be found here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html. They're very useful and typically match our intent when combining unions with other types, but in cases like these they end up transforming the resulting type into something entirely useless.

By wrapping the match in a single-element tuple, it prevents the
conditional from being checked distributively, which otherwise has the
effect of converting a union into an intersection when used with
contra-variant types.
@netlify
Copy link

netlify bot commented Apr 30, 2019

Deploy preview for redux-starter-kit-docs ready!

Built with commit 9c7ac4e

https://deploy-preview-138--redux-starter-kit-docs.netlify.com

@markerikson
Copy link
Collaborator

I'll similarly ask for help on this one. You think it's worth merging / ready to merge?

@hedgerh @Dudeonyx @denisw @Jessidhia

@hedgerh
Copy link

hedgerh commented May 20, 2019

Looks like @denisw may have more context here. I'll take some more time to look over this tonight if he's not available, though.

@denisw
Copy link
Contributor

denisw commented May 22, 2019

I didn't know about distributive conditional types - thanks for the pointer! The PR looks good to me. The only thing I'd add is a comment at the definition of SliceActionCreator explaining why the single-element tuple type is used, as it's far from obvious. :)

@tydira
Copy link
Author

tydira commented May 23, 2019

Cheers, @denisw. I didn't know about distributive conditionals either until I ran into this and wanted to figure out what was happening.

Does anyone have an explanatory comment in mind? I'm thinking something appended to the bottom of SliceActionCreator<P>'s original comment that says:

The P generic is wrapped with a single-element tuple to prevent the
conditional from being checked distributively, thus preserving unions
of contra-variant types.

I'll commit and push this if there are no objections.

@markerikson
Copy link
Collaborator

Sure, seems good. (I still don't quite grok the meaning, but sounds plausible :) )

@renatoruk
Copy link

renatoruk commented May 29, 2019

Thank you for this library, it really improves the workflow with redux. I am currently using it on a react typescript project for a short time, but can already see the benefit. 👏

I ran into the same issue as @kroogs with the first reducer I wrote using createSlice.

Slice instance looks like this:

export const module = createSlice({
    initialState: {
        visible: true,
    },
    reducers: {
        setVisible(state: ModuleState, action: PayloadAction<boolean>) {
            state.visible = action.payload;
        },
    },
});

Trying to dispatch that action from outside resulted with error:
TS: 2345 Argument of type 'boolean' is not assignable to parameter of type 'false & true'.

This is code in the container:

const visible = true;
dispatch(module.actions.setVisible(visible));

I just modified the dist node_modules for the time being as this PR fixes the problem (thank you @kroogs !), but it's only a temporary solution.

Is this PR ready to be merged? If not, what can we do to improve it?

@Retsam
Copy link
Contributor

Retsam commented Jun 12, 2019

As perhaps an alternate way of reaching the same end, perhaps:

// Since TS only distributes conditionals when the checked type is a naked type parameter
// intersecting with an empty interface will prevent it from distributing, without changing the contract of T
type NoDistribute<T> = T & {};

export type SliceActionCreator<P> = NoDistribute<P> extends void ? //...

If you wanted to link the docs on distributive conditional types for context in the comment, they're here: https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types.

@markerikson
Copy link
Collaborator

In the absence of further comments, I'm going to assume this is okay and go with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants