TypeScript - 使用专有参数键入函数的对象映射

Posted

技术标签:

【中文标题】TypeScript - 使用专有参数键入函数的对象映射【英文标题】:TypeScript - Typing an object map of functions with exclusive parameters 【发布时间】: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 似乎依靠推理效果很好。我之前一直在尽可能明确地写出所有代码风格和代码完整性,但似乎在某些情况下不明确输入有优势。 【参考方案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

【讨论】:

以上是关于TypeScript - 使用专有参数键入函数的对象映射的主要内容,如果未能解决你的问题,请参考以下文章

使用 Typescript 键入与另一个函数相同的函数

在 TypeScript 中键入匿名对象的属性

TS:使用其余参数键入高阶函数 ...args

强烈键入 react-redux 与 typescript 连接

Typescript:在这种情况下如何使用泛型类型键入函数?

Typescript:这种用于键入函数的键入方法是啥?