Home > Software engineering >  Ensure all values of enum type are covered within function body
Ensure all values of enum type are covered within function body

Time:05-19

I have a type that is:

export enum ApiFunctions {
  "setHidden" = "HIDE",
  "setReadOnly" = "SET_READ_ONLY",
  "setDescription" = "DESCRIPTION"
}
export type ValueOfApiFunction = `${ApiFunctions}`

and logic that is listening for an event on the window like so

window.addEventListener("message", (event) => {
                    if(event.data.type === "COMPLETE") {
                        event.data.changes.forEach((change: { fieldId: string, action: ValueOfApiFunction }) => {
                            const field = api.getFieldById(change.fieldId);

//I want to make sure that within this forEach all values of the type above are covered and if not, through a compilation error
// in this case "DESCRIPTION" isn't included so I would want a compilation error being thrown

                            if(change.action === "HIDE") {
                                field?.setVisible(false);
                            }
                            else if(change.action === "SET_READ_ONLY") {
                                field?.setName("This field is now read only")
                            }
                        })

                        resolve();
                    }
                    if(event.data.type === "MAKE_REQUEST") {
                        invoke('makeRequest', { url: event.data.url }).then(result => {
                            console.log(result);
                            iframe.contentWindow?.postMessage({
                                type: "REQUEST_COMPLETE",
                                response: result
                            }, "*")
                        });
                    }
                })

I have explained what I want to achieve in a comment in the code block above but basically I want to ensure all values of my type are covered within the forEach logic, if not, compilation error.

Many thanks

CodePudding user response:

TypeScript does not have a native capability to enforce the use of certain logic within a codebase. It is mostly concerned that objects are not manipulated incorrectly, rather than inspecting what the correct code is doing. Nevertheless, there is a workaround that would enable you to make sure that logic is executed for every possibility of an enum:

You can create a JSON object that holds all enum values as its keys, and each key has a value of a function, meaning that there is a piece of logic executed for every enum as follows:

const fieldToAction: { [key in ValueOfApiFunction]: (field?: Field) => void } = {
  "HIDE": (field?: Field) => field?.setVisible(false),
  "SET_READ_ONLY": (field?: Field) => field?.setName("This field is now read only"),
  "DESCRIPTION": (field?: Field) => field?.setName("This field is now read only"),
}

(The Field type could be something different as it was not provided in the example)

All you need to do now is to call this within your forEach as follows:

fieldToAction[change.action](field);

Playground link that illustrates this behaviour can be found here.

CodePudding user response:

You can take advantage of the 'never' type like this :

event.data.changes.forEach((change: { fieldId: string, action: ValueOfApiFunction }) => {
    const field = api.getFieldById(change.fieldId);
    if(change.action === "HIDE") {
        field?.setVisible(false);
    }
    else if(change.action === "SET_READ_ONLY") {
        field?.setName("This field is now read only")
    } else {
        const _shouldNeverBeReached: never = change.action
    }
    
})

Now if you forget to handle a change.action, the type will not be never and therefore it will not compile.

UPDATE : for a more explicite message:

const exaustiveCheck = <T extends any>(t: T extends never ? never : "One of the cases was not handled") => {
  throw new Error("One of the cases was not handled")
}

and then

    } else {
        exaustiveCheck(change.action)
    }
  • Related