Example problem: I have an Enum variable Difficulty
. In a function, I want to set the config DifficultyConfig
depending on the value of Difficulty
. Here's an inelegant way that I can think of:
export interface DifficultyConfig {
healthModifier: number,
deathIsPermanent: boolean,
}
export interface AppProps {
difficultyConfig: DifficultyConfig
}
export const NormalDifficultyConfig: DifficultyConfig = {
enemyHealthModifier: 1,
deathIsPermanent: false,
}
export const HigherDifficultyConfig: DifficultyConfig = {
enemyHealthModifier: 1.3,
deathIsPermanent: true,
}
export enum Difficulty {
NORMAL = 'Normal',
HARD = 'Hard',
ADVANCED = 'Advanced',
}
function createApp(difficulty: Difficulty) {
let difficultyConfig: DifficultyConfig;
switch(difficulty) {
case Difficulty.NORMAL:
difficultyConfig = NormalDifficultyConfig;
break;
// Both HARD and ADVANCED get HigherDifficultyConfig
case Difficulty.HARD:
difficultyConfig = HigherDifficultyConfig;
break;
case Difficulty.ADVANCED:
difficultyConfig = HigherDifficultyConfig;
break;
default:
difficultyConfig = NormalDifficultyConfig;
break;
}
return new App({
difficultyConfig
});
}
I'm not fond of the Switch case syntax for something so simple. My ideal would be something like this in Scala:
val difficultyConfig = difficulty match {
case Difficulty.NORMAL => NormalDifficultyConfig
case Difficulty.HARD | Difficulty.ADVANCED => HigherDifficultyConfig
case _ => NormalDifficultyConfig
}
Is there an equivalent for this in JavaScript?
CodePudding user response:
If there's a difference in logic, a switch
or an if
/else if
/else
(slightly less verbose) is probably the way to go (more on this below though). If it's purely data as in your example, then you could use a difficulty-to-config mapping object:
const difficultyConfigs = {
[Difficulty.NORMAL]: NormalDifficultyConfig,
[Difficulty.HARD]: HigherDifficultyConfig,
[Difficulty.ADVANCED]: HigherDifficultyConfig,
} as const;
You might even declare that as Record<Difficult, DifficultyConfig>
like this, as caTS points out in the comments:
const difficultyConfigs: Record<Difficult, DifficultyConfig> = {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Difficulty.NORMAL]: NormalDifficultyConfig,
[Difficulty.HARD]: HigherDifficultyConfig,
[Difficulty.ADVANCED]: HigherDifficultyConfig,
} as const;
More on that in a minute, but either way, then the function is just:
function createApp(difficulty: Difficulty) {
let difficultyConfig = difficultyConfigs[difficulty];
return new App({
difficultyConfig
});
}
Playground links: Without Record
| With Record
(I also updated a couple of seeming typos/editing errors in the question's code.)
This also has the advantage that if you add a Difficulty
but forget to include one in difficultyConfigs
, you get a handy compile-time error when you use it. I've simulated such an error here by adding a MEDIUM
difficulty but "forgetting" to update the function.
But better yet, if we include the type Record<Difficulty, DifficultyConfig>
type as caTS suggested and difficultyConfigs
doesn't have an entry for every Difficulty
value, you get an even earlier compile-time error, like this.
Even for logic, if you like you can use the same sort of concept to create a dispatch object:
const difficultyConfigs = {
[Difficulty.NORMAL]: () => { /*...build and return NORMAL config...*/ },
[Difficulty.HARD]: () => { /*...build and return HARD config...*/ },,
[Difficulty.ADVANCED]: () => { /*...build and return ADVANCED config...*/ },,
} as const;
// ...
const difficultyConfig = difficultyConfigs[difficulty]();