Saturday, June 2, 2018

Nameless interface with TypeScript

There are only two hard things in Computer Science: cache invalidation and naming things. -- PHil Karlton


Why give names to actions when your action and reducer can discriminate an action based on type (e.g., MyJobActionType) ?
export const enum MyJobActionType
{
    MY_JOB               = 'MY_JOB',

    MY_JOB_UI            = 'MY_JOB_UI',
    MY_JOB_DATA_FETCHING = 'MY_JOB_DATA_FETCHING',
    MY_JOB_DATA_FETCHED  = 'MY_JOB_DATA_FETCHED',

}

export type MyJobAction = IMyJobAction | IMyJobUIAction | IMyJobDataFetchingAction | IMyJobDataFetchedAction;

interface IMyJobAction extends Action
{
    type: MyJobActionType.MY_JOB;
    payload?: {
        jobId: 'new' | number;
    };
}

interface IMyJobUIAction extends Action
{
    type: MyJobActionType.MY_JOB_UI;
    ui: React.ComponentClass;
}

interface IMyJobDataFetchingAction extends Action
{
    type: MyJobActionType.MY_JOB_DATA_FETCHING;
}

interface IMyJobDataFetchedAction extends Action
{
    type: MyJobActionType.MY_JOB_DATA_FETCHED;
    data: IPagedDto<IPagedMyJobPostDto>;
}

Reducer:
export const myJobViewModelReducer = produce((
    draft: IMyJobViewModel = {
        view : null,
        model: {
            gridData: no.data
        }
    },
    action: MyJobAction
) =>


Action:
async function loadUI(dispatch: Dispatch<MyJobAction>)
{
    const component = (await import(/* webpackChunkName: 'myJob' */ './')).default;

    const uiAction: MyJobAction = {
        type: MyJobActionType.MY_JOB_UI,
        ui  : component
    };

    await dispatch(uiAction);
}


So don't name things then:
export type MyJobAction =
    {
        type: MyJobActionType.MY_JOB;
        payload?: {
            jobId: 'new' | number;
        };
    }
    |
    {
        type: MyJobActionType.MY_JOB_UI;
        ui: React.ComponentClass;
    }
    |
    {
        type: MyJobActionType.MY_JOB_DATA_FETCHING;
    }
    |
    {
        type: MyJobActionType.MY_JOB_DATA_FETCHED;
        data: IPagedDto<IPagedMyJobPostDto>;
    }
    ;

Are you worried someone might use the wrong payload (e.g., payload, ui, data) when dispatching an action? Don't worry, TypeScript is a smart language.




And just like that, TypeScript can infer that only the ui property is if a good discriminator is used, e.g., type: MyJobActionType.MY_JOB_UI




By the way, don't use the Dispatch type definition from redux, use the one from react-redux, it has more comprehensive type-checking. Upgraded to Redux 4, no more type definition problem.


So if you want TypeScript to type-check your direct object parameter to dispatch function, use react-redux's Dispatch type definition instead: Use Redux 4.0 instead, it comes with the correct type definition.



Finally, correct the properties of the object structure that matches type: MyJobActionType.MY_JOB_UI




Less interfaces, less names need to come up with.