Home > Blockchain >  Array of Enum values: check for Enum keys with Array.includes?
Array of Enum values: check for Enum keys with Array.includes?

Time:09-26

I have the following TypeScript Enum:

export enum Brennstoff {
    OEL = "Öl",
    GAS = "Gas",
    SOLAR = "Solar",
}

I have a multiselect input of Brennstoff that returns an array of the keys:

const selected = ["GAS", "SOLAR"]

How can I check if the array contains an entry? The following does not work

selected.includes(Brennstoff.GAS)

Playground link

Because Brennstoff.GAS return Gas, so it looks for the value, however I need to look for the key.

How can I do this in TypeScript?

CodePudding user response:

Introduction & TL;DR

Two solutions for you, both of which rely on a small bit of setup we'll go into below:

  1. Using a stricter type for selected so it can only contain valid Brennstoff keys, it looks like:

    console.log(selected.some(key => Brennstoff[key] === Brennstoff.GAS)); // true
    console.log(selected.some(key => Brennstoff[key] === Brennstoff.OEL)); // false
    
  2. Keeping selected as a string[], it looks like:

    console.log(selected.some(key => isBrennstoffKey(key) && Brennstoff[key] === Brennstoff.GAS)); // true
    console.log(selected.some(key => isBrennstoffKey(key) && Brennstoff[key] === Brennstoff.OEL)); // false
    

Validating keys

Both solutions involve validating that a string is a valid Brennstoff key, so let's look at that first:

You can use a type assertion function to validate keys. The type of a Brennstoff key is keyof typeof Brennstoff, but you may find it useful to have an alias for that:

type BrennstoffKey = keyof typeof Brennstoff;

Then the assertion function looks something like this:

const BrennstoffKeys = new Set(Object.keys(Brennstoff));
function assertBrennstoffKey(key: string): asserts key is BrennstoffKey {
    if (!BrennstoffKeys.has(key)) {
        throw new Error(`Invalid Brennstoff key "${key}"`);
    }
}

(You don't have to use the Set, you could call Object.keys every time you want to do the validation.)

I've taken the liberty of assuming each value has only one key.

I find I often want the type guard version as well as the assertion version; that combination would be something like:

const BrennstoffKeys = new Set(Object.keys(Brennstoff));
function isBrennstoffKey(key: string): key is BrennstoffKey {
    return BrennstoffKeys.has(key);
}
function assertBrennstoffKey(key: string): asserts key is BrennstoffKey {
    if (!isBrennstoffKey(key)) {
        throw new Error(`Invalid Brennstoff key "${key}"`);
    }
}

With that in place, let's look at two approaches...

Using a stricter type for selected

If selected is going to contain the key names from Brennstoff, then I suggest declaring it that way — BrennstoffKey[]:

const selected: BrennstoffKey[] = [];

Then you validate the strings before adding them:

let key: string = /*...*/;
assertBrennstoffKey(key);
selected.push(key);

To check if it has the key, since since the type of selected is strict enough, we can use some to see if it has the key for a particular value in it:

console.log(selected.some(key => Brennstoff[key] === Brennstoff.GAS)); // true
console.log(selected.some(key => Brennstoff[key] === Brennstoff.OEL)); // false

Playground link

With selected being string[]

If you want to keep selected as a string[], then we apply validation when doing the check instead:

If you're happy to do some up-front preparation (perhaps just after defining Brennstoff), it's fairly straightforward:

console.log(selected.some(key => isBrennstoffKey(key) && Brennstoff[key] === Brennstoff.GAS)); // true
console.log(selected.some(key => isBrennstoffKey(key) && Brennstoff[key] === Brennstoff.OEL)); // false

Playground link

  • Related