Home > Back-end >  In Typescript how do you assure a function parameter is one of a number of defined enums?
In Typescript how do you assure a function parameter is one of a number of defined enums?

Time:04-11

take this enum for example

export enum MyEnum {
  a = "a",
  b = "b",
  c = "c"
}

and then I define a function type where the parameter has to be one of these

i.e.

myFunction("c") is fine

myFunction("d") should throw an error

but obviously const myFunction = (param: string) would not work here

but also const myFunction = (param: MyEnum) does not work

CodePudding user response:

const f = (x: MyEnum) => …

…will work so long as you call f like this:

f(MyEnum.a)

It is possible to "import" or alias the enumeration values:

const { a } = MyEnum;
f(a);  // okay
const foo = MyEnum.b;
f(foo);  // okay

If you wish to use strings, (e.g. f('a')) you could use keyof typeof MyEnum:

const f = (x: keyof typeof MyEnum) => …
f('a');  // okay

…but if this is what you want, you may want to consider defining a union type instead:

type MyEnum = 'a' | 'b' | 'c';
const f = (x: MyEnum) => …
f('a');  // okay

CodePudding user response:

You can use like below

export enum MyEnum {
  a = "a",
    b = "b",
    c = "c"
}

const myFunction = (param: MyEnum) => {
  console.log(param)
}

myFunction(MyEnum.a)

CodePudding user response:

You can try the following:

   enum MyEnum {
      a = "a",
      b = "b",
      c = "c"
    }
    
    const myFunction = (param:MyEnum) =>{
        console.log(MyEnum[param]) //or MyEnym.param
    }
    
    myFunction("a") //will log a
    myFunction("d") //will log undefined

CodePudding user response:

The supported use case for enums in TypeScript is when you want a set of named constants and you want to refer to them exclusively by name. If you find yourself wanting to deal with the values of these enums without the name, then enums are not a great fit. In the enum

enum MyEnum {
  A = "a",
  B = "b",
  C = "c"
}

(I changed the keys because they are not in general identical to the values, but what follows is the same no matter what), it is intentionally forbidden for you to assign a string literal type like "a" or "b" to a value of the MyEnum type:

let x: MyEnum;
x = MyEnum.A; // okay
x = "a"; // error! Type '"a"' is not assignable to type 'MyEnum'.

const myFunction = (x: MyEnum) => { }
myFunction(MyEnum.A); // okay
myFunction("a"); // error! Type '"a"' is not assignable to type 'MyEnum'.

Yes, at runtime, MyEnum.A and "a" are the same value, but the TypeScript compiler takes the position that you should never be referring to the value of an enum except through the enum object itself.

Instead of fighting with or working around this restriction, you can give up on enums and just use a plain object instead:

const MyEnum = {
  A: "a",
  B: "b",
  C: "c"
} as const;

type MyEnum = typeof MyEnum[keyof typeof MyEnum];
//type MyEnum = "a" | "b" | "c"

let x: MyEnum;
x = MyEnum.A; // okay
x = "a"; // okay

const myFunction = (x: MyEnum) => { }
myFunction("a"); // okay
myFunction("z"); // error

Here we use a const assertion to tell the compiler to keep track of the string literal values in the MyEnum object. And now the MyEnum object and associated MyEnum type don't have any special enum branding to worry about. The type MyEnum is just the union of string literals you care about. You can write MyEnum.A if you want to, but you can also write "a"; they are equivalent, both at runtime and in the type system.


Still, if you must use an enum, there is a way to use template literal types to widen an enum type to corresponding string literal values:

enum MyEnum {
  A = "a",
  B = "b",
  C = "c"
}

type MyEnumStrings = `${MyEnum}`
// type MyEnumStrings = "a" | "b" | "c"

And then you can write your function in terms of that:

const myFunction = (x: MyEnumStrings) => { }
myFunction("a"); // okay
myFunction("z"); // error

But this doesn't work exactly for numeric enum values:

enum Whoops {
  A = "a",
  N = 123
}
type WhoopsStrings = `${Whoops}`
// type WhoopsStrings = "a" | "123"

And there won't be a way to do better until and unless something like microsoft/TypeScript#48094 is merged; this might happen for TypeScript 4.7, but I'm not sure.

Personally, I don't think it's worth this kind of hoop-jumping to get an off-label use of enums working, when you can just use a plain object instead. But that's up to you.

Playground link to code

  • Related