意外的隐式任何类型与通用类型联合

Unexpected implicit any type from union with generic type

我正在尝试动态推断方法参数,但由于某种原因,我得到了隐式推断的任何类型,即使我在悬停执行方法时确实看到了有效的方法定义,但在我重命名任一方法时它确实有效execute 以外的东西,但我宁愿让它们保持不变,我怎样才能做到这一点?

type CommandBase<T extends BaseCommandInteraction> = ApplicationCommandData & {
  execute(interaction: T): unknown | Promise<unknown>
}

export type ContextMenuCommand = CommandBase<ContextMenuInteraction<"cached">> &
  (UserApplicationCommandData | MessageApplicationCommandData)

export type SlashCommand = CommandBase<CommandInteraction<"cached">> &
  ChatInputApplicationCommandData

type ExtraLegacyCommandData<TArgs> = {
  resolveArgs(args: Args, message: Message): TArgs
}

type LegacyCommand<TArgs> = {
  type: "LEGACY"
  name: string
  description: string
  execute(message: Message, args: TArgs): unknown | Promise<unknown>
  // eslint-disable-next-line @typescript-eslint/ban-types
} & (TArgs extends void ? {} : ExtraLegacyCommandData<TArgs>)

export type Command<TArgs> =
  | ContextMenuCommand
  | SlashCommand
  | LegacyCommand<TArgs>

export function createCommand<TArgs = void>(command: Command<TArgs>) {
  return command
}

export default createCommand({
  type: "LEGACY",
  name: "",
  description: "",
  execute(message) {
    // Type signature shows `execute(message: Message<boolean>, args: void): unknown`
    // But `Parameter 'message' implicitly has an 'any' type.`
  },
})

Link to playground

编辑:我认为当我定义两个预期的函数参数时推理确实有效,我试图避免必须这样做:

type LegacyCommand<TArgs> = {
  type: "LEGACY"
  name: string
  description: string
  execute(
    message: Message,
    ...args: TArgs extends void ? [] : [TArgs]
  ): unknown | Promise<unknown>
  // eslint-disable-next-line @typescript-eslint/ban-types
} & (TArgs extends void ? {} : ExtraLegacyCommandData<TArgs>)

但这也行不通:

// (method) execute(message: Message<boolean>): unknown
async execute(message) { // Parameter 'message' implicitly has an 'any' type.

我发现我的问题与 https://github.com/microsoft/TypeScript/wiki/Breaking-Changes#generic-type-parameters-are-implicitly-constrained-to-unknown 有关,为了解决它,我为我的函数提供了一个明确的约束并稍微调整了我的执行方法

type LegacyCommand<TArgs> = {
  type: "LEGACY"
  name: string
  description: string
  execute: (
    message: Message,
    // Only exposing arguments when array of known type is passed as generic
    ...args: TArgs extends unknown[] ? [TArgs] : []
  ) => unknown | Promise<unknown>
  // Checking if tuple generic was passed in
} & (TArgs extends [unknown] ? ExtraLegacyCommandData<TArgs> : {})

// Explicit constraint
export function createCommand<TArgs extends unknown[] = []>(
  command: Command<TArgs>
) {
  return command
}