Home > Enterprise >  Typescript enforce that string value is one of the object values
Typescript enforce that string value is one of the object values

Time:06-02

I have this constant declaration:

export const Actions = {
    VIEW: 'view',
    EDIT: 'edit',
};

and lets assume now I have a function like below:

// how to ensure action variable below is string of value either view or edit
function insertAction(action: string): void {
  console.log('trust me i inserted the action');
}

My goal is to allow action parameter to have values only view | edit and understand this *dynamically, so please do not suggest using union solution because I know that already and is not fit to my purpose.

I have tried signature like the one below

function insertAction(action: typeof Actions[keyof typeof Actions]

but it does not work.

I can still call the function insertAction('whatever') and ts compile does not fail neither the linter.

In my package.json am using typescript version 4.4.2 with PHPStorm Editor and nextjs

    "typescript": "^4.4.2"

CodePudding user response:

Your current solution:

function insertAction(action: typeof Actions[keyof typeof Actions])

should work, as long as you declare the Actions object using the as const modifier:

export const Actions = {
    VIEW: 'view',
    EDIT: 'edit',
} as const; // <-- note the assertion here.

This works because by default, when you declare an object without an explicit type, eg:

export const Actions = {
    VIEW: 'view',
    EDIT: 'edit',
}

TypeScript can see that the variable is constant, and cannot be reassigned. However since it is assigned an object with potentially mutable values, TypeScript will infer for that object the type:

{
  VIEW: string;
  EDIT: string;
}

What you want is for the value type not to be string, but the exact literal string type for "view" and "edit". Using the as const modifier informs TypeScript that the values of the object will not be mutated, and so it will then infer the stricter, literal string type for those values.

Then, since the value types are the exact strings "view" and "edit" respectively, using typeof Actions[keyof typeof Actions] will produce the union type "view" | "edit", exactly what is needed for your parameter type.

CodePudding user response:

why not use enum and assign action:EAction?

That should look like this:

export enum EActions {
    VIEW = 'view',
    EDIT = 'edit',
}

your function

function insertAction(action: EAction): void {
  console.log('trust me i inserted the action');
}

will show you type error:

insertAction('whatever'); // will show you error

Argument of type '"whatever"' is not assignable to parameter of type 'EAction'.

Use your EActions:

insertAction(EAction.VIEW);

It would be easier to read in my opinion.

Example: JS Playground

  • Related