How to type function taking an enum How to type function taking an enum typescript typescript

How to type function taking an enum


There is another one approach to figure out whether initial value is valid or not.

As you already know, TS might treat enum as a number or as an object or typeof enum. In the similar way typescript treats classes.

We need some how to obtain numerical keys of enum.

Let's try to iterate through enum keys:

enum MyEnum {    ONE,    TWO}type Enumerate<Enum extends number | string> = keyof {    [Prop in Enum]: Prop}// non generic version type Keys = keyof typeof MyEnumtype Enumerate2 = keyof {    [Prop in Keys]: Prop}type Result = Enumerate<MyEnum> // MyEnum, not good

It does not work, because TS is smart enought to figure out that we are iterating over enum keys. Hence we are getting MyEnum instead of 0 | 1.

We can wrap Prop key into a string to cheat typescript.

enum MyEnum {    ONE,    TWO}type Enumerate<Enum extends number | string> = keyof {    [Prop in `${Enum}`]: Prop}type Result = Enumerate<MyEnum> // "0" | "1"

Much better now. But it is still not what we want. It is impossible to extract number from a string in current version of typescript in generic way.

But we always can compare string to number which is wrapped in string during the comparison.I mean something like that: "0" extends ${number} ? ...`Above code is perfectly valid.

enum MyEnum {    ONE,    TWO}type Enumerate<Enum extends number | string> = keyof {    [Prop in `${Enum}`]: Prop}type Result = Enumerate<MyEnum> // "0" | "1"type Values<T> = T[keyof T]type IsKeyValid<InitialValue extends number, Enum extends Record<string | number, string | number>> =    `${InitialValue}` extends Enumerate<Values<Enum>> ? InitialValue : neverfunction useMyFun<    Enum extends Record<string | number, string | number>,    InitialValue extends number,    >(anEnum: Enum, initialState: IsKeyValid<InitialValue, Enum>) { }useMyFun(MyEnum, MyEnum.ONE) // okuseMyFun(MyEnum, 0) // okuseMyFun(MyEnum, -1) // erroruseMyFun(MyEnum, NaN) // error

Playground

Enum - is infered type of enum

InitialValue - is infered type of second argument.

IsKeyValid - is an utility type which checks whether wrapped into string InitialValue is equal to allowed enum keys or not. If it equal - return InitialValue, otherwise return never

P.S. Related question with React component props


Let me preface this by saying that I highly advise against doing this, but for the sake of fun, here's a hack to get the behavior you're looking for. This solution effectively makes the enum's values into an opaque type.

enum _MyEnum {  ONE,  TWO,}declare const __brand: unique symbol;type MyEnum = {  [K in keyof typeof _MyEnum]: (typeof _MyEnum)[K] & {    readonly [__brand]: never;  };};const MyEnum: MyEnum = _MyEnum as any;type StringKeyOf<T> = Extract<keyof T, string>;type EnumNumber<E> = Record<StringKeyOf<E>, number>;function useMyFun<E extends EnumNumber<E>, V extends E[keyof E]>(  anEnum: E,  initialState: V): { value: V; setValue: (v: V) => void } {  const [value, setValue] = useState<V>(initialState as V);  //.... more stuff using both arguments omitted  MyEnum.ONE.toFixed;  MyEnum.ONE;  return { value, setValue };}useMyFun(MyEnum, MyEnum.ONE);useMyFun(MyEnum, -1);


your way have an question.When Enum has '0',it can pass 0 to the function.

type EnumToNumber<T extends number, arr extends number[] = []>    = `${T}` extends `-${infer N}` ? never :    T extends arr['length'] ? arr['length']    : EnumToNumber<T, [...arr, 1]>type SafeEnum<T extends Record<string | number, string | number>,    >    = {        [key in keyof T]: T[key] extends number ? EnumToNumber<T[key]> : `${T[key]}`    }[keyof T]

it can work with string enum.but i don't find a way to transform negative number string literal to negative number literal

enum E{  A=0,  B=1,  C='0'}type E=SafeEnum<typeof E> //0,1,'0'