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

Use of TypeScript generics breaks in Actions #442

Closed
jmattheis opened this issue Aug 24, 2020 · 1 comment
Closed

Use of TypeScript generics breaks in Actions #442

jmattheis opened this issue Aug 24, 2020 · 1 comment
Assignees
Labels
bug Something isn't working

Comments

@jmattheis
Copy link

I want to use generics in overmind actions, but this doesn't work correctly because the actions will be resolved without the generics.

Here an example.

state.ts

export enum Setting {
    Theme = 'theme',
    HideHeader = 'hideHeader',
}

type SettingState = {
    settings: Settings;
};

export type Settings = {
    [Setting.Theme]: 'dark' | 'light';
    [Setting.HideHeader]: boolean;
};

export const state: SettingState = {
    settings: {
        [Setting.Theme]: 'dark',
        [Setting.HideHeader]: false,
    },
};

actions.ts

import {Action, Config, IContext} from 'overmind';
import {Setting, Settings} from './state';

export const set = <T extends Setting>({state}: IContext<Config>, {key, value}: {key: T; value: Settings[T]}): void => {
    state.settings = {...state.settings, [key]: value};
};

export const test: Action = ({actions}) => {
    // should not compile but does
    // value takes type: 'dark' | 'light' | boolean;
    actions.set({key: Setting.Theme, value: true})

    // is correct
    actions.set({key: Setting.HideHeader, value: true})
    
    // should not compile but does
    actions.set({key: Setting.HideHeader, value: 'dark'})
};

With some workarounds it is possible to get it working but it doesn't look really good:

actions.ts@workaround

import {Action, Config, IContext} from 'overmind';
import {Setting, Settings} from './state';

// When returning a method with a generic type it does work, but we have to execute the state change inside an action therefore
// we need runInAction
export const set = ({actions}: IContext<Config>, _: void) => <T extends Setting>({key, value}: {key: T; value: Settings[T]}): void => {
    actions.settings.runInAction(({state}) => {
        state.settings = {...state.settings, [key]: value};
    })
};

export const runInAction: Action<(ctx: IContext<Config>) => void> = (context, callback) => callback(context);

export const test: Action = ({actions}) => {
    // does not compile: true is not assignable to 'dark' | 'light'
    actions.set()({key: Setting.Theme, value: true})

    // is correct
    actions.set()({key: Setting.HideHeader, value: true})

    // does not compile
    actions.set()({key: Setting.HideHeader, value: 'dark'})
};

Is there a better way to do this? We have an application that is heavily dependent on this pattern thus a more optimal solution would be awesome.

@jmattheis jmattheis added the bug Something isn't working label Aug 24, 2020
@christianalfoni
Copy link
Member

The documentation is updated with a different default official typing pattern now, which supports generics. Let me know if you need any more information: https://overmindjs.org/core/typescript

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants