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
selected
so it can only contain validBrennstoff
keys, it looks like:console.log(selected.some(key => Brennstoff[key] === Brennstoff.GAS)); // true console.log(selected.some(key => Brennstoff[key] === Brennstoff.OEL)); // false
Keeping
selected
as 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