首页 > 解决方案 > 在打字稿的列表中映射正确函数的函数签名

问题描述

我正在构建一个消息格式化程序,它可以构建一堆不同类型的消息。根据它正在构建的消息类型,将调用不同的渲染函数。这些函数中的每一个都可以接受不同的参数或可选参数。

我在设置这个并且让打字稿理解我在做什么时遇到了麻烦。

这是一些杂乱的代码,可以让您了解我所追求的。

export enum MessageType {
  Greeting,
  Warning,
}

getFormattedMessage(messageType:MessageType, ...messageParams): string {
  const messageBuilders: {[K in MessageType]: () => string} = {
    [MessageType.Greeting]: () => 'Hi There',
    [ErrorType.Warning]: ({name}) =>
      name ? `${name} is required` : 'That thing is required',
  }

  return messageBuilders[messageType](...messageParams)
}

getFormattedMessage(MessageType.Greeting)
// output, "Hi There
getFormattedMessage(MessageType.Warning)
// output, "That thing is required"
getFormattedMessage(MessageType.Warning, {name: "A fancy hat"})
// output, "A fancy hat is required"

上面的代码有一些问题。例如,我在为getFormattedMessage或正确设置打字稿签名时遇到了麻烦messageBuilders

我想达到一个点,如果我getFormattedMessage(MessageType.Warning, 在我的编辑器中输入,vsCode 会建议显示在附加到Warning类型的渲染函数的签名中的变量,依此类推。

非常感谢!

标签: typescript

解决方案


这里使用了一个辅助标识函数newMessageBuilder,让编译器隐式推断 messageBuilder 的类型,同时约束其类型的下限;然后根据 的值Parameters<(typeof messageBuilders)[K]>来约束 的类型。messageParamsmessageType

export enum MessageType {
  Greeting,
}
export enum ErrorType {
  Warning = 1,
}

function newMessageBuilder<T extends {[K in MessageType | ErrorType]: (...messageParams: any[]) => string}>(messageBuilders: T) { return messageBuilders; }
const messageBuilders = newMessageBuilder({
  [MessageType.Greeting]: () => 'Hi There',
  [ErrorType.Warning]: ({name}: {name: string}) =>
    name ? `${name} is required` : 'That thing is required',
});

function getFormattedMessage<K extends MessageType | ErrorType>(messageType: K, ...messageParams: Parameters<(typeof messageBuilders)[K]>)  {
  return (messageBuilders[messageType] as any)(...messageParams);
}

getFormattedMessage(MessageType.Greeting)
// output, "Hi There

// ERROR Argument of type '{ a: number; }' is not assignable to parameter of type '{ name: string; }'.
// Object literal may only specify known properties, and 'a' does not exist in type '{ name: string; }'.(2345)
getFormattedMessage(ErrorType.Warning, {a:1})
// output, "That thing is required"

getFormattedMessage(ErrorType.Warning, {name: "A fancy hat"})
// output, "A fancy hat is required"

当然,上述问题仍然存在(messageBuilders[messageType] as any)忽略的问题,因为我发现没有解决方案留给读者作为练习。

游乐场链接


推荐阅读