Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to type function taking an enum

Given some enum MyEnum {ONE, TWO}, I wanted to write a function called like

useMyFun(MyEnum, MyEnum.ONE);

and I failed to type it properly. Now I have something like the following

type StringKeyOf<T> = Extract<keyof T, string>;
type EnumNumber<E> = Record<StringKeyOf<E>, number>;
function useMyFun<E extends EnumNumber<E>, V extends number=number> (
    anEnum: E,
    initialState: number) : {value: V, setValue: (v: V) => void}
{
    const [value, setValue] = useState<V>(initialState as V);
    //.... more stuff using both arguments omitted    
    return {value, setValue};
}

It's a react-hook, but this doesn't matter as all you need to compile it is a dummy

function useState<V>(initialState: V) {
    const result: [V, (v: V) => void] = [initialState, v => { }];
    return result;
}

It works (using the current typescript version), but it allows me to call useMyFun(MyEnum, -1) as well, which is wrong. Note that I only care about enums like above, i.e., enums with default numerical values, no specified values and no const modifier.

*I also need that the return type has value: MyEnum rather than number.


I know that the runtime value of MyEnum is {0: 'ONE', 1: 'TWO', ONE: '0', TWO: '1'} which means that the above typing is actually wrong. However, this was the only way how to get the first argument compile. The second argument when dealing with MyEnum should actually be 0 | 1, but I can't get it working.

I really need both the enum object and the value in the function. Can someone get the types right?

like image 434
maaartinus Avatar asked Jun 08 '20 17:06

maaartinus


People also ask

How do you pass enum value to a function in C++?

static class Myclass { ... public: enum encoding { BINARY, ASCII, ALNUM, NUM }; Myclass(Myclass::encoding); ... } Then in the method definition: Myclass::Myclass(Myclass::encoding enc) { ... }

Can you use an enum as a type?

Just like other data types in TypeScript, we can use enums as function parameters or return types, like this: enum Weekend { Friday = 1, Saturday, Sunday } function getDate(Day: string): Weekend { if ( Day === 'TGIF') { return Weekend.Friday; } } let DayType: Weekend = getDate('TGIF');

Can enum be a return type or function?

enum is a data type, not a function.

How do I string an enum?

There are two ways to convert an Enum to String in Java, first by using the name() method of Enum which is an implicit method and available to all Enum, and second by using toString() method.

What is enum in Java?

In Java (from 1.5), enums are represented using enum data type. Java enums are more powerful than C/C++ enums. In Java, we can also add variables, methods and constructors to it. The main objective of enum is to define our own data types (Enumerated Data Types).

How do you declare a type from an enum?

Using an enum is simple: just access any member as a property off of the enum itself, and declare types using the name of the enum: // ... respond ("Princess Caroline", UserResponse. Yes ); Numeric enums can be mixed in computed and constant members (see below) .

How do enum values get assigned to string values?

In addition to each member of the enum becoming a property of the object (CardinalDirection = 1]), the enum also creates a key for each number and assigns the string as the value. In the case of North, CardinalDirection = 1 returns the value 1, and CardinalDirection = "North" assigns the value "North" to the key "1".

How do I use an enum in Swift?

Using an enum is simple: just access any member as a property off of the enum itself, and declare types using the name of the enum: enum Response { No = 0, Yes = 1, } function respond(recipient: string, message: Response): void { // ...


Video Answer


1 Answers

Related question you can find here

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 MyEnum
type 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 : never

function useMyFun<
    Enum extends Record<string | number, string | number>,
    InitialValue extends number,
    >(anEnum: Enum, initialState: IsKeyValid<InitialValue, Enum>) { }

useMyFun(MyEnum, MyEnum.ONE) // ok
useMyFun(MyEnum, 0) // ok

useMyFun(MyEnum, -1) // error
useMyFun(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

like image 138
captain-yossarian Avatar answered Oct 18 '22 12:10

captain-yossarian