How to get names of enum entries? - enums

I would like to iterate a TypeScript enum object and get each enumerated symbol name, for example:
enum myEnum { entry1, entry2 }
for (var entry in myEnum) {
// use entry's name here, e.g., "entry1"
}

Though the answer is already provided, Almost no one pointed to the docs
Here's a snippet
enum Enum {
A
}
let nameOfA = Enum[Enum.A]; // "A"
Keep in mind that string enum members do not get a reverse mapping generated at all.

The code you posted will work; it will print out all the members of the enum, including the values of the enum members. For example, the following code:
enum myEnum { bar, foo }
for (var enumMember in myEnum) {
console.log("enum member: ", enumMember);
}
Will print the following:
Enum member: 0
Enum member: 1
Enum member: bar
Enum member: foo
If you instead want only the member names, and not the values, you could do something like this:
for (var enumMember in myEnum) {
var isValueProperty = Number(enumMember) >= 0
if (isValueProperty) {
console.log("enum member: ", myEnum[enumMember]);
}
}
That will print out just the names:
Enum member: bar
Enum member: foo
Caveat: this slightly relies on an implementation detail: TypeScript compiles enums to a JS object with the enum values being members of the object. If TS decided to implement them different in the future, the above technique could break.

For me an easier, practical and direct way to understand what is going on, is that the following enumeration:
enum colors { red, green, blue };
Will be converted essentially to this:
var colors = { red: 0, green: 1, blue: 2,
[0]: "red", [1]: "green", [2]: "blue" }
Because of this, the following will be true:
colors.red === 0
colors[colors.red] === "red"
colors["red"] === 0
This creates a easy way to get the name of an enumerated as follows:
var color: colors = colors.red;
console.log("The color selected is " + colors[color]);
It also creates a nice way to convert a string to an enumerated value.
var colorName: string = "green";
var color: colors = colors.red;
if (colorName in colors) color = colors[colorName];
The two situations above are far more common situation, because usually you are far more interested in the name of a specific value and serializing values in a generic way.

If you only search for the names and iterate later use:
Object.keys(myEnum).map(key => myEnum[key]).filter(value => typeof value === 'string') as string[];

Assuming you stick to the rules and only produce enums with numeric values, you can use this code. This correctly handles the case where you have a name that is coincidentally a valid number
enum Color {
Red,
Green,
Blue,
"10" // wat
}
var names: string[] = [];
for(var n in Color) {
if(typeof Color[n] === 'number') names.push(n);
}
console.log(names); // ['Red', 'Green', 'Blue', '10']

It seems that none of the answers here will work with string-enums in strict-mode.
Consider enum as:
enum AnimalEnum {
dog = "dog", cat = "cat", mouse = "mouse"
}
Accessing this with AnimalEnum["dog"] may result in an error like:
Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'typeof AnimalEnum'.ts(7053).
Proper solution for that case, write it as:
AnimalEnum["dog" as keyof typeof AnimalEnum]

With current TypeScript Version 1.8.9 I use typed Enums:
export enum Option {
OPTION1 = <any>'this is option 1',
OPTION2 = <any>'this is option 2'
}
with results in this Javascript object:
Option = {
"OPTION1": "this is option 1",
"OPTION2": "this is option 2",
"this is option 1": "OPTION1",
"this is option 2": "OPTION2"
}
so I have to query through keys and values and only return values:
let optionNames: Array<any> = [];
for (let enumValue in Option) {
let optionNameLength = optionNames.length;
if (optionNameLength === 0) {
this.optionNames.push([enumValue, Option[enumValue]]);
} else {
if (this.optionNames[optionNameLength - 1][1] !== enumValue) {
this.optionNames.push([enumValue, Option[enumValue]]);
}
}
}
And I receive the option keys in an Array:
optionNames = [ "OPTION1", "OPTION2" ];

As of TypeScript 2.4, enums can contain string intializers https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html
This allows you to write:
enum Order {
ONE = "First",
TWO = "Second"
}
console.log(`One is ${Order.ONE.toString()}`);
and get this output:
One is First

This solution work too.
enum ScreenType {
Edit = 1,
New = 2,
View = 4
}
var type: ScreenType = ScreenType.Edit;
console.log(ScreenType[type]); //Edit

In a nutshell
if your enums is as below:
export enum Colors1 {
Red = 1,
Green = 2,
Blue = 3
}
to get specific text and value:
console.log(Colors1.Red); // 1
console.log(Colors1[Colors1.Red]); // Red
to get list of value and text:
public getTextAndValues(e: { [s: number]: string }) {
for (const enumMember in e) {
if (parseInt(enumMember, 10) >= 0) {
console.log(e[enumMember]) // Value, such as 1,2,3
console.log(parseInt(enumMember, 10)) // Text, such as Red,Green,Blue
}
}
}
this.getTextAndValues(Colors1)
if your enums is as below:
export enum Colors2 {
Red = "Red",
Green = "Green",
Blue = "Blue"
}
to get specific text and value:
console.log(Colors2.Red); // Red
console.log(Colors2["Red"]); // Red
to get list of value and text:
public getTextAndValues(e: { [s: string]: string }) {
for (const enumMember in e) {
console.log(e[enumMember]);// Value, such as Red,Green,Blue
console.log(enumMember); // Text, such as Red,Green,Blue
}
}
this.getTextAndValues(Colors2)

Another interesting solution found here is using ES6 Map:
export enum Type {
low,
mid,
high
}
export const TypeLabel = new Map<number, string>([
[Type.low, 'Low Season'],
[Type.mid, 'Mid Season'],
[Type.high, 'High Season']
]);
USE
console.log(TypeLabel.get(Type.low)); // Low Season
TypeLabel.forEach((label, value) => {
console.log(label, value);
});
// Low Season 0
// Mid Season 1
// High Season 2

Let ts-enum-util (github, npm) do the work for you and provide a lot of additional type-safe utilities. Works with both string and numeric enums, properly ignoring the numeric index reverse lookup entries for numeric enums:
String enum:
import {$enum} from "ts-enum-util";
enum Option {
OPTION1 = 'this is option 1',
OPTION2 = 'this is option 2'
}
// type: ("OPTION1" | "OPTION2")[]
// value: ["OPTION1", "OPTION2"]
const keys= $enum(Option).getKeys();
// type: Option[]
// value: ["this is option 1", "this is option 2"]
const values = $enum(Option).getValues();
Numeric enum:
enum Option {
OPTION1,
OPTION2
}
// type: ("OPTION1" | "OPTION2")[]
// value: ["OPTION1", "OPTION2"]
const keys= $enum(Option).getKeys();
// type: Option[]
// value: [0, 1]
const values = $enum(Option).getValues();

In TypeScript, an enum is compiled to a map (to get the value from the key) in javascript:
enum MyEnum {
entry0,
entry1,
}
console.log(MyEnum['entry0']); // 0
console.log(MyEnum['entry1']); // 1
It also creates a reversed map (to get the key from the value):
console.log(MyEnum[0]); // 'entry0'
console.log(MyEnum[0]); // 'entry1'
So you can access the name of an entry by doing:
console.log(MyEnum[MyEnum.entry0]); // 'entry0'
console.log(MyEnum[MyEnum.entry1]); // 'entry1'
However, string enum has no reverse map by design (see comment and pull request) because this could lead to conflict between keys and values in the map object.
enum MyEnum {
entry0 = 'value0',
entry1 = 'value1',
}
console.log(MyEnum['value0']); // undefined
console.log(MyEnum['value1']); // undefined
If you want to force your string enum to compile a reversed map (you then have to make sure all the keys and values are different), you can use this trick:
enum MyEnum {
entry0 = <any>'value0',
entry1 = <any>'value1',
}
console.log(MyEnum['value0']); // 'entry0'
console.log(MyEnum['value1']); // 'entry1'
console.log(MyEnum[MyEnum.entry0]); // 'entry0'
console.log(MyEnum[MyEnum.entry1]); // 'entry1'

I got tired looking through incorrect answers, and did it myself.
THIS ONE HAS TESTS.
Works with all types of enumerations.
Correctly typed.
type EnumKeys<Enum> = Exclude<keyof Enum, number>
const enumObject = <Enum extends Record<string, number | string>>(e: Enum) => {
const copy = {...e} as { [K in EnumKeys<Enum>]: Enum[K] };
Object.values(e).forEach(value => typeof value === 'number' && delete copy[value]);
return copy;
};
const enumKeys = <Enum extends Record<string, number | string>>(e: Enum) => {
return Object.keys(enumObject(e)) as EnumKeys<Enum>[];
};
const enumValues = <Enum extends Record<string, number | string>>(e: Enum) => {
return [...new Set(Object.values(enumObject(e)))] as Enum[EnumKeys<Enum>][];
};
enum Test1 { A = "C", B = "D"}
enum Test2 { A, B }
enum Test3 { A = 0, B = "C" }
enum Test4 { A = "0", B = "C" }
enum Test5 { undefined = "A" }
enum Test6 { A = "undefined" }
enum Test7 { A, B = "A" }
enum Test8 { A = "A", B = "A" }
enum Test9 { A = "B", B = "A" }
console.log(enumObject(Test1)); // {A: "C", B: "D"}
console.log(enumObject(Test2)); // {A: 0, B: 1}
console.log(enumObject(Test3)); // {A: 0, B: "C"}
console.log(enumObject(Test4)); // {A: "0", B: "C"}
console.log(enumObject(Test5)); // {undefined: "A"}
console.log(enumObject(Test6)); // {A: "undefined"}
console.log(enumObject(Test7)); // {A: 0,B: "A"}
console.log(enumObject(Test8)); // {A: "A", B: "A"}
console.log(enumObject(Test9)); // {A: "B", B: "A"}
console.log(enumKeys(Test1)); // ["A", "B"]
console.log(enumKeys(Test2)); // ["A", "B"]
console.log(enumKeys(Test3)); // ["A", "B"]
console.log(enumKeys(Test4)); // ["A", "B"]
console.log(enumKeys(Test5)); // ["undefined"]
console.log(enumKeys(Test6)); // ["A"]
console.log(enumKeys(Test7)); // ["A", "B"]
console.log(enumKeys(Test8)); // ["A", "B"]
console.log(enumKeys(Test9)); // ["A", "B"]
console.log(enumValues(Test1)); // ["C", "D"]
console.log(enumValues(Test2)); // [0, 1]
console.log(enumValues(Test3)); // [0, "C"]
console.log(enumValues(Test4)); // ["0", "C"]
console.log(enumValues(Test5)); // ["A"]
console.log(enumValues(Test6)); // ["undefined"]
console.log(enumValues(Test7)); // [0, "A"]
console.log(enumValues(Test8)); // ["A"]
console.log(enumValues(Test9)); // ["B", "A"]
Online version.

Assume you have an enum
export enum SCROLL_LABEL_OFFSET {
SMALL = 48,
REGULAR = 60,
LARGE = 112
}
and you want to create a type based on enum but not just copy and paste.
You could use an enum to create your type like this:
export type ScrollLabelOffset = keyof typeof SCROLL_LABEL_OFFSET;
In result you will receive a type with possible values as 'SMALL' | 'REGULAR' | 'LARGE'

Starting from TypeScript 2.4, the enum would not contain the key as a member anymore. source from TypeScript readme
The caveat is that string-initialized enums can't be reverse-mapped to get the original enum member name. In other words, you can't write Colors["RED"] to get the string "Red".
My solution:
export const getColourKey = (value: string ) => {
let colourKey = '';
for (const key in ColourEnum) {
if (value === ColourEnum[key]) {
colourKey = key;
break;
}
}
return colourKey;
};

Based on some answers above I came up with this type-safe function signature:
export function getStringValuesFromEnum<T>(myEnum: T): (keyof T)[] {
return Object.keys(myEnum).filter(k => typeof (myEnum as any)[k] === 'number') as any;
}
Usage:
enum myEnum { entry1, entry2 };
const stringVals = getStringValuesFromEnum(myEnum);
the type of stringVals is 'entry1' | 'entry2'
See it in action

According to TypeScript documentation, we can do this via Enum with static functions.
Get Enum Name with static functions
enum myEnum {
entry1,
entry2
}
namespace myEnum {
export function GetmyEnumName(m: myEnum) {
return myEnum[m];
}
}
now we can call it like below
myEnum.GetmyEnumName(myEnum.entry1);
// result entry1
for reading more about Enum with static function follow the below link
https://basarat.gitbooks.io/typescript/docs/enums.html

I wrote an EnumUtil class which is making a type check by the enum value:
export class EnumUtils {
/**
* Returns the enum keys
* #param enumObj enum object
* #param enumType the enum type
*/
static getEnumKeys(enumObj: any, enumType: EnumType): any[] {
return EnumUtils.getEnumValues(enumObj, enumType).map(value => enumObj[value]);
}
/**
* Returns the enum values
* #param enumObj enum object
* #param enumType the enum type
*/
static getEnumValues(enumObj: any, enumType: EnumType): any[] {
return Object.keys(enumObj).filter(key => typeof enumObj[key] === enumType);
}
}
export enum EnumType {
Number = 'number',
String = 'string'
}
How to use it:
enum NumberValueEnum{
A= 0,
B= 1
}
enum StringValueEnum{
A= 'A',
B= 'B'
}
EnumUtils.getEnumKeys(NumberValueEnum, EnumType.Number);
EnumUtils.getEnumValues(NumberValueEnum, EnumType.Number);
EnumUtils.getEnumKeys(StringValueEnum, EnumType.String);
EnumUtils.getEnumValues(StringValueEnum, EnumType.String);
Result for NumberValueEnum keys: ["A", "B"]
Result for NumberValueEnum values: [0, 1]
Result for StringValueEnumkeys: ["A", "B"]
Result for StringValueEnumvalues: ["A", "B"]

They have provided a concept called 'reverse-mapping' in their official documentation. It helped me:
https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
The solution is quite straight forward:
enum Enum {
A,
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

Having numeric enum:
enum MyNumericEnum {
First = 1,
Second = 2
}
You need to convert it to array first:
const values = Object.values(MyNumericEnum);
// ['First', 'Second', 1, 2]
As you see, it contains both keys and values. Keys go first.
After that, you can retrieve its keys:
values.slice(0, values.length / 2);
// ['First', 'Second']
And values:
values.slice(values.length / 2);
// [1, 2]
For string enums, you can use Object.keys(MyStringEnum) in order to get keys and Object.values(MyStringEnum) in order to get values respectively.
Though it's somewhat challenging to extract keys and values of mixed enum.

There are already a lot of answers here but I figure I'll throw my solution onto the stack anyway.
TypeScript Playground
enum AccountType {
Google = 'goo',
Facebook = 'boo',
Twitter = 'wit',
}
type Key = keyof typeof AccountType // "Google" | "Facebook" | "Twitter"
// this creates a POJO of the enum "reversed" using TypeScript's Record utility
const reversed = (Object.keys(AccountType) as Key[]).reduce((acc, key) => {
acc[AccountType[key]] = key
return acc
}, {} as Record<AccountType, string>)
For Clarity:
/*
* reversed == {
* "goo": "Google",
* "boo": "Facebook",
* "wit": "Twitter",
* }
* reversed[AccountType.Google] === "Google" 👍
*/
Reference for TypeScript Record
To iterate:
for (const [key, value] of Object.entries(reversed)) {
console.log(`${key}: ${value}`);
}
A nice helper function:
const getAccountTypeName = (type: AccountType) => {
return reversed[type]
};
// getAccountTypeName(AccountType.Twitter) === 'Twitter'

The only solution that works for me in all cases (even if values are strings) is the following :
var enumToString = function(enumType, enumValue) {
for (var enumMember in enumType) {
if (enumType[enumMember]==enumValue) return enumMember
}
}

You can use the enum-values package I wrote when I had the same problem:
Git: enum-values
var names = EnumValues.getNames(myEnum);

I found this question by searching "TypeScript iterate over enum keys". So I just want to post solution which works for me in my case. Maybe it'll help to someone too.
My case is the following: I want to iterate over each enum key, then filter some keys, then access some object which has keys as computed values from enum. So this is how I do it without having any TS error.
enum MyEnum = { ONE = 'ONE', TWO = 'TWO' }
const LABELS = {
[MyEnum.ONE]: 'Label one',
[MyEnum.TWO]: 'Label two'
}
// to declare type is important - otherwise TS complains on LABELS[type]
// also, if replace Object.values with Object.keys -
// - TS blames wrong types here: "string[] is not assignable to MyEnum[]"
const allKeys: Array<MyEnum> = Object.values(MyEnum)
const allowedKeys = allKeys.filter(
(type) => type !== MyEnum.ONE
)
const allowedLabels = allowedKeys.map((type) => ({
label: LABELS[type]
}))

You can get an array of names from Enum in this way:
const enumNames: string[] = Object.keys(YourEnum).filter(key => isNaN(Number(key)));

Old question, but, why do not use a const object map?
Instead of doing this:
enum Foo {
BAR = 60,
EVERYTHING_IS_TERRIBLE = 80
}
console.log(Object.keys(Foo))
// -> ["60", "80", "BAR", "EVERYTHING_IS_TERRIBLE"]
console.log(Object.values(Foo))
// -> ["BAR", "EVERYTHING_IS_TERRIBLE", 60, 80]
Do this (pay attention to the as const cast):
const Foo = {
BAR: 60,
EVERYTHING_IS_TERRIBLE: 80
} as const
console.log(Object.keys(Foo))
// -> ["BAR", "EVERYTHING_IS_TERRIBLE"]
console.log(Object.values(Foo))
// -> [60, 80]

If you have enum
enum Diet {
KETO = "Ketogenic",
ATKINS = "Atkins",
PALEO = "Paleo",
DGAF = "Whatever"
}
Then you can get key and values like:
Object.keys(Diet).forEach((d: Diet) => {
console.log(d); // KETO
console.log(Diet[d]) // Ketogenic
});

I find that solution more elegant:
for (let val in myEnum ) {
if ( isNaN( parseInt( val )) )
console.log( val );
}
It displays:
bar
foo

To get the list of the enum values you have to use:
enum AnimalEnum {
DOG = "dog",
CAT = "cat",
MOUSE = "mouse"
}
Object.values(AnimalEnum);

Related

Is there a way to directly access a field value in an enum struct without pattern matching?

I wish that enums in Rust can be used like Haskell's productive type. I want to
access a field's value directly
assign a field's value directly or make a clone with the changing value.
Directly means that not using too long pattern matching code, but just could access like let a_size = a.size.
In Haskell:
data TypeAB = A {size::Int, name::String} | B {size::Int, switch::Bool} deriving Show
main = do
let a = A 1 "abc"
let b = B 1 True
print (size a) -- could access a field's value directly
print (name a) -- could access a field's value directly
print (switch b) -- could access a field's value directly
let aa = a{size=2} -- could make a clone directly with the changing value
print aa
I tried two styles of Rust enum definition like
Style A:
#[derive(Debug)]
enum EntryType {
A(TypeA),
B(TypeB),
}
#[derive(Debug)]
struct TypeA {
size: u32,
name: String,
}
#[derive(Debug)]
struct TypeB {
size: u32,
switch: bool,
}
fn main() {
let mut ta = TypeA {
size: 3,
name: "TAB".to_string(),
};
println!("{:?}", &ta);
ta.size = 2;
ta.name = "TCD".to_string();
println!("{:?}", &ta);
let mut ea = EntryType::A(TypeA {
size: 1,
name: "abc".to_string(),
});
let mut eb = EntryType::B(TypeB {
size: 1,
switch: true,
});
let vec_ab = vec![&ea, &eb];
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
// Want to do like `ta.size = 2` for ea
// Want to do like `ta.name = "bcd".to_string()` for ea
// Want to do like `tb.switch = false` for eb
// ????
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
}
Style B:
#[derive(Debug)]
enum TypeCD {
TypeC { size: u32, name: String },
TypeD { size: u32, switch: bool },
}
fn main() {
// NOTE: Rust requires representative struct name before each constructor
// TODO: Check constructor name can be duplicated
let mut c = TypeCD::TypeC {
size: 1,
name: "abc".to_string(),
};
let mut d = TypeCD::TypeD {
size: 1,
switch: true,
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
// Can't access a field's value like
// let c_size = c.size
let c_size = c.size; // [ERROR]: No field `size` on `TypeCD`
let c_name = c.name; // [ERROR]: No field `name` on `TypeCD`
let d_switch = d.switch; // [ERROR]: No field `switch` on `TypeCD`
// Can't change a field's value like
// c.size = 2;
// c.name = "cde".to_string();
// d.switch = false;
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
}
I couldn't access/assign values directly in any style. Do I have to implement functions or a trait just to access a field's value? Is there some way of deriving things to help this situation?
What about style C:
#[derive(Debug)]
enum Color {
Green { name: String },
Blue { switch: bool },
}
#[derive(Debug)]
struct Something {
size: u32,
color: Color,
}
fn main() {
let c = Something {
size: 1,
color: Color::Green {
name: "green".to_string(),
},
};
let d = Something {
size: 2,
color: Color::Blue { switch: true },
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
let _ = c.size;
}
If all variant have something in common, why separate them?
Of course, I need to access not common field too.
This would imply that Rust should define what to do when the actual type at runtime doesn't contain the field you required. So, I don't think Rust would add this one day.
You could do it yourself. It will require some lines of code, but that matches the behavior of your Haskell code. However, I don't think this is the best thing to do. Haskell is Haskell, I think you should code in Rust and not try to code Haskell by using Rust. That a general rule, some feature of Rust come directly from Haskell, but what you want here is very odd in my opinion for Rust code.
#[derive(Debug)]
enum Something {
A { size: u32, name: String },
B { size: u32, switch: bool },
}
impl Something {
fn size(&self) -> u32 {
match self {
Something::A { size, .. } => *size,
Something::B { size, .. } => *size,
}
}
fn name(&self) -> &String {
match self {
Something::A { name, .. } => name,
Something::B { .. } => panic!("Something::B doesn't have name field"),
}
}
fn switch(&self) -> bool {
match self {
Something::A { .. } => panic!("Something::A doesn't have switch field"),
Something::B { switch, .. } => *switch,
}
}
fn new_size(&self, size: u32) -> Something {
match self {
Something::A { name, .. } => Something::A {
size,
name: name.clone(),
},
Something::B { switch, .. } => Something::B {
size,
switch: *switch,
},
}
}
// etc...
}
fn main() {
let a = Something::A {
size: 1,
name: "Rust is not haskell".to_string(),
};
println!("{:?}", a.size());
println!("{:?}", a.name());
let b = Something::B {
size: 1,
switch: true,
};
println!("{:?}", b.switch());
let aa = a.new_size(2);
println!("{:?}", aa);
}
I think there is currently no built-in way of accessing size directly on the enum type. Until then, enum_dispatch or a macro-based solution may help you.

Typescript: Declare function that takes parameter of exact interface type, and not one of its derived types

interface I {
a: number;
}
interface II extends I {
b: number;
}
function f(arg: I) : void {
// do something with arg without trimming the extra properties (logical error)
console.log(arg);
}
const obj: II = { a:4, b:3 };
f(obj);
What I want to do is to make function f to accept only objects of type I and not type II or any other derived interface
Difficult because of the way typescript works. What you can do is add a type field to the base, which a derived interface would override. Then to limit a function to only accept the base explicitly:
interface IFoo<T extends string = "foo"> {
type: T;
}
interface IBar extends IFoo<"bar"> {
}
function ray(baseOnly: IFoo<"foo">) {
}
let foo: IFoo = { type: "foo" };
let bar: IBar = { type: "bar" };
ray(foo); // OK!
ray(bar); // error
and the output error:
[ts]
Argument of type 'IBar' is not assignable to parameter of type 'IFoo<"foo">'.
Types of property 'type' are incompatible.
Type '"bar"' is not assignable to type '"foo"'.
You cannot achieve this in Typescript, in general, in most languages you cannot make such a constraint. One principle of object oriented programming is that you can pass a derived class where a base class is expected. You can perform a runtime check and if you find members that you don't expect, you can throw an error. But the compiler will not help you achieve this.
This works for me (on ts 3.3 anyway):
// Checks that B is a subset of A (no extra properties)
type Subset<A extends {}, B extends {}> = {
[P in keyof B]: P extends keyof A ? (B[P] extends A[P] | undefined ? A[P] : never) : never;
}
// Type for function arguments
type Strict<A extends {}, B extends {}> = Subset<A, B> & Subset<B, A>;
// E.g.
type BaseOptions = { a: string, b: number }
const strict = <T extends Strict<BaseOptions, T>>(options: T) => { }
strict({ a: "hi", b: 4 }) //Fine
strict({ a: 5, b: 4 }) //Error
strict({ a: "o", b: "hello" }) //Error
strict({ a: "o" }) //Error
strict({ b: 4 }) //Error
strict({ a: "o", b: 4, c: 5 }) //Error
// Type for variable declarations
type Exact<A extends {}> = Subset<A, A>;
// E.g.
const options0: Exact<BaseOptions> = { a: "hi", b: 4 } //Fine
const options1: Exact<BaseOptions> = { a: 5, b: 4 } //Error
const options2: Exact<BaseOptions> = { a: "o", b: "hello" } //Error
const options3: Exact<BaseOptions> = { a: "o" } //Error
const options4: Exact<BaseOptions> = { b: 4 } //Error
const options5: Exact<BaseOptions> = { a: "o", b: 4, c: 5 } //Error
// Beware of using Exact for arguments:
// For inline arguments it seems to work correctly:
exact({ a: "o", b: 4, c: 5 }) //Error
strict({ a: "o", b: 4, c: 5 }) //Error
// But it doesn't work for arguments coming from variables:
const options6 = { a: "o", b: 4, c: 5 }
exact(options6) // Fine -- Should be error
strict(options6) //Error -- Is correctly error
You can see more detail in my comment here.
So applied to your example:
interface I { a: number; }
interface II extends I { b: number; }
function f<T extends Strict<I, T>>(arg: T): void {
// do something with arg without trimming the extra properties (logical error)
console.log(arg);
}
const obj1: I = { a: 4 };
const obj2: II = { a: 4, b: 3 };
f(obj1); // Fine
f(obj2); // Error
Another possibility is to give up on interfaces and use classes with private properties and private constructors. These discourage extension:
export class I {
private clazz: 'I'; // private field
private constructor(public a: number) {
Object.seal(this); // if you really don't want extra properties at runtime
}
public static make(a: number): I {
return new I(a); // can only call new inside the class
}
}
let i = I.make(3);
f(i); // okay
You can't create an I as an object literal:
i = { a: 2 }; // error, isn't an I
f({a: 2}); // error, isn't an I
You can't subclass it:
class II extends I { // error, I has a private constructor
b: number;
}
You can extend it via interface:
interface III extends I {
b: number;
}
declare let iii: III;
and you can call the function on the extended interface
f(iii);
but you still can't create one with an object literal
iii = { a: 1, b: 2 }; // error
or with destructuring (which creates a new object also),
iii = { ...I.make(1), b: 2 };
, so this is at least somewhat safer than using interfaces.
There are ways around this for crafty developers. You can get TypeScript to make a subclass via Object.assign(), but if you use Object.seal() in the constructor of I you can at least get an error at runtime:
iii = Object.assign(i, { b: 17 }); // no error at compile time, error at runtime
And you can always silence the type system with any, (although again, you can use an instanceof guard inside f() to cause an error at runtime).
iii = { a: 1, b: 2 } as any; // no error
f(iii); // no error at compile time, maybe error if f() uses instanceof
Hope that helps; good luck!

Modifying a value inside an enum while matching

Is it possible to directly modify a value embedded inside an enum?
The following fails with error: cannot borrow immutable anonymous field `a.0` as mutable, even though I used ref mut.
enum Foo {
Bar(usize),
}
fn main() {
let a = Foo::Bar(10);
match a {
Foo::Bar(ref mut val) => *val = 33,
}
match a {
Foo::Bar(val) => println!("{}", val), // should print 33
}
}
That's not a huge problem because I can do the following as a work-around:
match a {
Foo::Bar(val) => a = Foo::Bar(33),
}
But is this the correct way?
You need to make the binding to a mutable.
enum Foo {
Bar(usize),
}
fn main() {
let mut a = Foo::Bar(10);
match a {
Foo::Bar(ref mut val) => *val = 33,
}
match a {
Foo::Bar(val) => println!("{}", val), // 33
}
}

Enum as Parameter in TypeScript

Isn't it possible to set the type of a parameter to an Enum? Like this:
private getRandomElementOfEnum(e : enum):string{
var length:number = Object.keys(e).length;
return e[Math.floor((Math.random() * length)+1)];
}
Following error is thrown:
Argument expression expected.(1135)
With any obviously everything is alright:
private getRandomElementOfEnum(e : any):string{
var length:number = Object.keys(e).length;
return e[Math.floor((Math.random() * length)+1)];
}
Is there a possibility or a little workaround to define an enum as a parameter?
It's not possible to ensure the parameter is an enum, because enumerations in TS don't inherit from a common ancestor or interface.
TypeScript brings static analysis. Your code uses dynamic programming with Object.keys and e[dynamicKey]. For dynamic codes, the type any is convenient.
Your code is buggy: length() doesn't exists, e[Math.floor((Math.random() * length)+1)] returns a string or an integer, and the enumeration values can be manually set…
Here is a suggestion:
function getRandomElementOfEnum<E>(e: any): E {
var keys = Object.keys(e),
index = Math.floor(Math.random() * keys.length),
k = keys[index];
if (typeof e[k] === 'number')
return <any>e[k];
return <any>parseInt(k, 10);
}
function display(a: Color) {
console.log(a);
}
enum Color { Blue, Green };
display(getRandomElementOfEnum<Color>(Color));
Ideally, the parameter type any should be replaced by typeof E but the compiler (TS 1.5) can't understand this syntax.
You can do better than any:
enum E1 {
A, B, C
}
enum E2 {
X, Y, Z
}
function getRandomElementOfEnum(e: { [s: number]: string }): number {
/* insert working implementation here */
return undefined;
}
// OK
var x: E1 = getRandomElementOfEnum(E1);
// Error
var y: E2 = getRandomElementOfEnum(window);
// Error
var z: string = getRandomElementOfEnum(E2);
I agree with #Tarh. Enums in TypeScript are just Javascript objects without a common interface or prototype (and if they are const enum, then they are not even objects), so you cannot restrict types to "any enum".
The closest I could get is something like the following:
enum E1 {
A, B, C
}
enum E2 {
X, Y, Z
}
// make up your own interface to match TypeScript enums
// as closely as possible (not perfect, though)
interface Enum {
[id: number]: string
}
function getRandomElementOfEnum(e: Enum): string {
let length = Object.keys(e).length / 2;
return e[Math.floor((Math.random() * length))];
}
This works for all enums (without custom initializers), but it would also accept other arrays as input (and then fail because the method body relies on the very specific key structure that TypeScript enums have).
So unless you have a real need for such a "generic" function, make typesafe functions for the individual enum types (or a union type like E1|E2|E3) that you actually need.
And if you do have this need (and this might very well be an X-Y-problem that can be solved in a better, completely different way given more context), use any, because you have left typesafe territory anyway.
Summing up the previous answers with some new syntax - a generic typesafe function, which works with numeric enums as well as string enums:
function getRandomElementOfEnum<T extends {[key: number]: string | number}>(e: T): T[keyof T] {
const keys = Object.keys(e);
const randomKeyIndex = Math.floor(Math.random() * keys.length);
const randomKey = keys[randomKeyIndex];
// Numeric enums members also get a reverse mapping from enum values to enum names.
// So, if a key is a number, actually it's a value of a numeric enum.
// see https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
const randomKeyNumber = Number(randomKey);
return isNaN(randomKeyNumber)
? e[randomKey as keyof T]
: randomKeyNumber as unknown as T[keyof T];
}
Another possible option not mentioned above is using the actual values. This is however possible only when you know all the options. This, in my opinion is definitely better than any.
doSomething(a: string, b: 'this'|'can'|'work'): void {
//do something
}
Tested on TypeScript 3.9.7
Solution
type EnumTypeString<TEnum extends string> =
{ [key in string]: TEnum | string; }
type EnumTypeNumber<TEnum extends number> =
{ [key in string]: TEnum | number; }
| { [key in number]: string; }
type EnumType<TEnum extends string | number> =
(TEnum extends string ? EnumTypeString<TEnum> : never)
| (TEnum extends number ? EnumTypeNumber<TEnum> : never)
type EnumOf<TEnumType> = TEnumType extends EnumType<infer U>
? U
: never
Usage
EnumType:
function forEachEnum<TEnum extends string | number>(
enumType: EnumType<TEnum>,
callback: (value: TEnum, key: string) => boolean|void,
) {
for (let key in enumType) {
if (Object.prototype.hasOwnProperty.call(enumType, key) && isNaN(Number(key))) {
const value = enumType[key] as any
if (callback(value, key)) {
return
}
}
}
}
EnumOf:
function forEachEnum2<TEnumType>(
enumType: TEnumType,
callback: (value: EnumOf<TEnumType>, key: string) => boolean|void,
) {
for (let key in enumType) {
if (Object.prototype.hasOwnProperty.call(enumType, key) && isNaN(Number(key))) {
const value = enumType[key] as any
if (callback(value, key)) {
return
}
}
}
}
Tests
enum EnumAsString {
Value1 = 'value 1',
Value2 = 'value 2',
}
enum EnumAsNumber {
Value1 = 1,
Value2 = 2,
}
// Error
let sn: EnumType<string> = EnumAsNumber
// Correct
let ns: EnumType<number> = EnumAsString // I have not found a solution for the error here
let nn: EnumType<number> = EnumAsNumber
let Nn: EnumType<EnumAsNumber> = EnumAsNumber
let ss: EnumType<string> = EnumAsString
let Ss: EnumType<EnumAsString> = EnumAsString
forEachEnum(EnumAsString, value => {
let e: EnumAsString = value
let s: string = value
let n: number = value // Error
})
forEachEnum(EnumAsNumber, value => {
let e: EnumAsNumber = value
let s: string = value // Error
let n: number = value
})
forEachEnum2(EnumAsString, value => {
let e: EnumAsString = value
let s: string = value
let n: number = value // Error
})
forEachEnum2(EnumAsNumber, value => {
let e: EnumAsNumber = value
let s: string = value // Error
let n: number = value
})
May be this trick will fit:
enum AbstractEnum { // put somewhere in hidden scope
}
private getRandomElementOfEnum(e: typeof AbstractEnum) {
...
}
#selinathat's solution is great only if you have few types. but what if we have more ? for example :
doSomething(a: string, b: 'this'|'can'|'work'|'test1'|'test2'|'test3'): void {
//do something
}
its pretty ugly hah !?
i prefer to use keyof :
interface Items {
'this',
'can',
'work',
'test1',
'test2',
'test3',
}
doSomething(a: string, b: keyof Items): void {
//do something
}
Here is an example that allows passing an enum with a typechecked value of that enum using a generic. It's really a response to a slightly different question here that was marked as a duplicate: Typescript how to pass enum as Parameter
enum Color {
blue,
};
enum Car {
cadillac,
};
enum Shape {
square,
}
type SupportedEnums = typeof Color | typeof Car;
type InvertTypeOf<T> = T extends typeof Color ? Color :
T extends typeof Car ? Car : never;
function getText<T extends SupportedEnums>(enumValue: InvertTypeOf<T>, typeEnum: T) string | undefined {
if (typeEnum[enumValue]) {
return `${enumValue}(${typeEnum[enumValue]})`;
}
}
console.log(getText(Car.cadillac, Car)); // 0(cadillac)
console.log(getText(0, Color)); // 0(red)
console.log(getText(4, Color)); // undefined
// #ts-expect-error Color is not Car
console.log(getText(Color.blue, Car));
// #ts-expect-error Car is not a Color
console.log(getText(Car.toyota, Color));
// #ts-expect-error Shape is not in SupportedEnums
console.log(getText(5, Shape));
// #ts-expect-error Shape is not in SupportedEnums
console.log(getText(Shape.square, Shape));
I had the same kind of problem, and i did this
private getOptionsFromEnum(OptionEnum: Record<string, string>): Array<SelectOption> {
return Object.keys(OptionEnum).map((value) => {
return {
name: OptionEnum[value],
value,
} as SelectOption;
});
}
I made a helper type to accept any enum as a paramaeter, then you can handle whatever you need next with Object or by calling an index of the Enum.
type Enum = Record<string | number, string | number>
Now use it to accept any enum as parameter:
function enumValues<T extends Enum>(enum: T, filter?: "string"): string[];
function enumValues<T extends Enum>(enum: T, filter?: "number"): number[];
function enumValues<T extends Enum>(enum: T, filter?: undefined): (string|number)[];
function enumValues<T extends Enum>(enum: T, filter?: "string" | "number") {
return Object.values(enum).filter(x => !filter || typeof x === filter);
}
enum color {
red,
green,
blue
}
console.log(enumValues(color,"string"));
// output ['red','green','blue']
console.log(enumValues(color,"number"));
// output [0,1,2]

Enums with data in swift

I would like to use java-like enums, where you can have enum instances with custom data. For instance:
enum Country {
case Moldova(capital: "Chișinău", flagColors: [Color.Blue, Color.Yellow, Color.Red]);
case Botswana(capital: "Gaborone", flagColors: [Color.Blue, Color.White, Color.Black]);
}
I could later write:
Country.Moldova.capital;
It seems that I can indicate the variables, but not the values, and I can only assign the values when using the enum, not declaring. Which would be the best way to mimic this behaviour?
you can do something like this, which may be helpful: (that is a very generic example only)
enum Country : Int {
case Moldova, Botwana;
//
func capital() -> String {
switch (self) {
case .Moldova:
return "Chișinău"
case .Botwana:
return "Gaborone"
default:
return ""
}
}
//
func flagColours() -> Array<UIColor> {
switch (self) {
case .Moldova:
return [UIColor.blueColor(), UIColor.yellowColor(), UIColor.redColor()]
case .Botwana:
return [UIColor.blueColor(), UIColor.whiteColor(), UIColor.blackColor()]
default:
return []
}
}
//
func all() -> (capital: String, flagColours: Array<UIColor>) {
return (capital(), flagColours())
}
//
var capitolName: String {
get {
return capital()
}
}
//
var flagColoursArray: Array<UIColor> {
get {
return flagColours()
}
}
}
then you can access to the details like this:
let country: Country = Country.Botwana
get the capital
that way:
let capital: String = country.capital()
or another:
let capital: String = country.all().capital
or a third one:
let capital: String = country.capitolName
get the flag's colours:
that way:
let flagColours: Array<UIColor> = country.flagColours()
or another:
let flagColours: Array<UIColor> = country.all().flagColours
or a third one:
let flagColours: Array<UIColor> = country.flagColoursArray
Here is another generic example that is similar to the one posted by holex, but a bit closer to how it looks in Java. (I like it because all of the custom data is in one place). Instead of switching on 'self' in separate methods, I simply create a static dictionary that maps each case to a tuple containing the appropriate data. Then, I have convenience var's to get at the data easily.
enum TestEnum {
case One
case Two
case Three
private static let associatedValues = [
One: (int: 1, double: 1.0, string: "One"),
Two: (int: 2, double: 2.0, string: "Two"),
Three: (int: 3, double: 3.0, string: "Three")
]
var int: Int {
return TestEnum.associatedValues[self]!.int;
}
var double: Double {
return TestEnum.associatedValues[self]!.double;
}
var string: String {
return TestEnum.associatedValues[self]!.string;
}
}
You can access the custom data like so:
println(TestEnum.One.int) // 1
println(TestEnum.Two.double) // 2.0
println(TestEnum.Three.string) // Three
Unfortunately it seems like enums with raw values are limited to literal values. You may want to file a bug.
As an alternative, you could do something like this:
let Country = (
Moldova: (capital: "Chișinău", flagColors: [Color.Blue, Color.Yellow, Color.Red]),
Botswana: (capital: "Gaborone", flagColors: [Color.Blue, Color.White, Color.Black])
)
or this:
struct Country {
let capital: String
let flagColors: [Color]
}
let Countries = (
Moldova: Country(capital: "Chișinău", flagColors: [.Blue, .Yellow, .Red]),
Botswana: Country(capital: "Gaborone", flagColors: [.Blue, .White, .Black])
)

Resources