Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Inferring the correct type with Generics

I’m trying to create a reducer function that allows us to infer the allowed properties and value in a payload, and this currently works, if the type property in the dispatch is changed, it will allow for only the values that are associated with the ToolMap to be passed and will throw errors if wrong name value is passed in payload.

The issue occurs in the receiving end of the reducer, the action.payload can’t properly infer that the name property is correct, it always thinks it a union type of either CursorToolName or DrawingToolName. Which makes me a bit confused as when the payload reaches that case in the switch statement it should always be the correct type.

type ToolCategory = 'CursorTools' | 'DrawingTools'
type CursorToolName = 'Cursor' | 'Measure'
type DrawingToolName = 'Brush' | 'Pencil'


type ToolMap = {
    CursorTools: CursorToolName;
    DrawingTools: DrawingToolName
}

interface ToolState {
    CursorTools: Tool<'CursorTools'>
    DrawingTools: Tool<'DrawingTools'>
}

interface ToolAction<T extends ToolCategory>{
    type: T;
    payload: Tool<T>
}

interface Tool<T extends ToolCategory> {
    name: ToolMap[T];
    onEnter?: () => void;
}

const reducer = <T extends ToolCategory>(
    state: ToolState,
    action: ToolAction<T>
): ToolState => {
    switch(action.type){
        case 'CursorTools': {
            return {
                ...state,
                CursorTools: {
                    // This doesn't infer that the payload.name is of correct type
                    name: action.payload.name
                }
            }
        }
    }

    return state;
}

// This infers the name in the payload we can pass, and we can never pass the wrong value
reducer({CursorTools: {name: 'Cursor'}, DrawingTools: {name: 'Brush'}
}, {type: 'CursorTools', payload: {
    name: 'Cursor'
}})

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The following would do the trick:

const reducer = <T extends ToolCategory, S extends ToolState>(
    state: S,
    action: ToolAction<T>
): S => {
    switch(action.type){
        case 'CursorTools': {
            return {
                ...state,
                CursorTools: {
                    name: action.payload.name
                }
            }
        }
    }

    return state;
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading