【问题标题】:TypeScript - Typing an object map of functions with exclusive parametersTypeScript - 使用专有参数键入函数的对象映射
【发布时间】:2022-01-23 18:16:45
【问题描述】:

这是一个我通过键保存函数的对象:

export const questSetHelper: { [key in QuestSetHelper]: (player: Player, payload: FlagGender | FlagOrigin | FlagAdventure) => void } = {
  'setGender': setGender,
  'setOriginCircumstance': setOriginCircumstance,
  'setAdventureCircumstance': setAdventureCircumstance
}

我将它们保存为字符串,以便于处理从前端到后端的传递字符串,这些字符串用于确定要运行的函数。每个函数只处理一种负载类型。

例如:

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')
}

当所有这些都输入到string 时,我当然没有任何类型错误,但我希望显式输入每个函数参数。

问题 显然,每个函数最终都可以处理payloadquestSetHelper 类型之一,但我想假设每个函数都将被正确调用并传递正确的类型(即FlagGender 有效负载将始终转到setGender)。

有没有更好的方法在不改变结构的情况下输入这个?

【问题讨论】:

  • 你能依赖类型推断吗?它将为questSetHelper 分配一个正确、精确的类型。如果不是,请解释原因。 typescriptlang.org/play?#code/…
  • 泛型也应该可以工作。
  • @Lesiak 似乎依靠推理效果很好。我之前一直在尽可能明确地写出所有代码风格和代码完整性,但似乎在某些情况下不明确输入有优势。

标签: typescript typescript-typings


【解决方案1】:

我相信有两种选择:

依赖类型推断

这是对您的代码的一个小改动

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

使用显式类型确保所有 setter 具有正确的类型

在这种方法中,您从定义QuestFlags 开始。 之后,您使用Key Remapping in Mapped Types 生成具有适当名称的设置器:

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

我看到您将蛇形大小写用于属性名称,而将驼峰大小写用于设置器。这也很容易实现:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-30
    • 2020-02-02
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多