Here is an object where I keep functions by key:
export const questSetHelper: { [key in QuestSetHelper]: (player: Player, payload: FlagGender | FlagOrigin | FlagAdventure) => void } = {
'setGender': setGender,
'setOriginCircumstance': setOriginCircumstance,
'setAdventureCircumstance': setAdventureCircumstance
}
I keep them as strings for easy handling from front-end to back-end passing strings that are used to determine which function to run. Each function handles only one of the payload types.
For example:
export const setOriginCircumstance = (player: Player, payload: FlagOrigin): void => {
if (player.quests['intro']?.flags)
player.quests['intro'].flags['origin_circumstance'] = payload
else
throw new Error('Quest: intro - failed setOriginCircumstance')
}
When all these were typed to string, I had no type errors of course, but I want to have each of these function parameters explicitly typed.
PROBLEM
Obviously each function can ultimately handle ONE of the questSetHelper types of payload, but I want to assume that each function will be called correctly and passed the correct type (i.e. FlagGender payload will always go to setGender).
Is there a better way to type this without changing the structure?
CodePudding user response:
I believe there are 2 options:
Rely on type inference
This is a minor change to your code
export const setGender = (player: Player, payload: FlagGender): void => {}
export const setOriginCircumstance = (player: Player, payload: FlagOrigin): void => {}
export const setAdventureCircumstance = (player: Player, payload: FlagAdventure): void => {}
export const questSetHelper = {
'setGender': setGender,
'setOriginCircumstance': setOriginCircumstance,
'setAdventureCircumstance': setAdventureCircumstance
}
Use explicit typing to ensure all setters have correct types
In this approach, you start with defining QuestFlags.
After that, you use Key Remapping in Mapped Types to generate setters with appropriate names:
interface QuestFlags {
gender: FlagGender
originCircumstance: FlagOrigin
adventureCircumstance: FlagAdventure
}
type QuestFlagsSetters = {
[K in keyof QuestFlags & string as `set${Capitalize<K>}`]: (player: Player, payload: QuestFlags[K]) => void
};
export const questSetHelper: QuestFlagsSetters = {
'setGender': setGender,
'setOriginCircumstance': setOriginCircumstance,
'setAdventureCircumstance': setAdventureCircumstance
}
I see you use snake case for property names but camel case for setters. This is easy to achieve as well:
interface QuestFlags {
gender: FlagGender
origin_circumstance: FlagOrigin
adventure_circumstance: FlagAdventure
}
type SnakeCaseToPascalCase<S extends string> =
S extends `${infer FirstWord}_${infer Rest}` ?
`${Capitalize<Lowercase<FirstWord>>}${SnakeCaseToPascalCase<Rest>}` :
Capitalize<Lowercase<S>>;
type QuestFlagsSetters = {
[K in keyof QuestFlags & string as `set${SnakeCaseToPascalCase<K>}`]: (player: Player, payload: QuestFlags[K]) => void
};
export const questSetHelper: QuestFlagsSetters = {
'setGender': setGender,
'setOriginCircumstance': setOriginCircumstance,
'setAdventureCircumstance': setAdventureCircumstance
}
Playground - Key remapping with snake_case changed to camelCase
