1

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?

3
  • 2
    Can you rely on type inference? It will assign a correct, precise type to questSetHelper. If not, please explain why. typescriptlang.org/play?#code/… Commented Dec 22, 2021 at 14:33
  • generics should also work. Commented Dec 22, 2021 at 14:35
  • @Lesiak It seems like relying on inference works well. I had previously been writing out everything explicitly as much as possible for both coding style and code integrity, but it seems like there are some cases where not typing explicitly has advantages. Commented Dec 22, 2021 at 14:48

1 Answer 1

2

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
}

Playground - inference

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
}

Playground - Key remapping

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.