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)
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:
Using a stricter type for
selectedso it can only contain validBrennstoffkeys, it looks like:console.log(selected.some(key => Brennstoff[key] === Brennstoff.GAS)); // true console.log(selected.some(key => Brennstoff[key] === Brennstoff.OEL)); // falseKeeping
selectedas astring[], 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
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
