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?
static class Myclass { ... public: enum encoding { BINARY, ASCII, ALNUM, NUM }; Myclass(Myclass::encoding); ... } Then in the method definition: Myclass::Myclass(Myclass::encoding enc) { ... }
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');
enum is a data type, not a function.
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.
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).
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) .
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".
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 { // ...
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With