You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Typescript 如何推导键值对类型实现 Discord.js 动态事件正确类型校验

实现方案

1. 重定义IBotEvent泛型接口

首先修改你的IBotEvent接口定义,通过泛型将事件名和对应的参数类型绑定:

import type { ClientEvents } from "discord.js";

// 泛型K约束为ClientEvents的键,默认为所有事件名的联合类型
export interface IBotEvent<K extends keyof ClientEvents = keyof ClientEvents> {
  name: K;
  once: boolean;
  execute: (...args: ClientEvents[K]) => Promise<void>;
}

// 可选:添加事件定义工厂函数,实现完全自动类型推导
export function defineEvent<K extends keyof ClientEvents>(event: IBotEvent<K>) {
  return event;
}

2. 编写事件文件(两种可选写法)

推荐写法:工厂函数+对象字面量(无需手动指定泛型)

直接用刚才定义的defineEvent包裹事件配置,TS会根据name字段自动推导execute的参数类型,完全不需要手动写参数类型注解:

import { defineEvent } from "../interfaces";

export default defineEvent({
  name: "ready",
  once: true,
  // 自动推导参数为Client<true>类型,无需手动声明
  execute: async (client) => {
    console.log(`登录成功,账号:${client.user.tag}`);
  }
});

如果是多参数的事件同样适用,比如emojiUpdate:

export default defineEvent({
  name: "emojiUpdate",
  once: false,
  // 自动推导两个参数分别为GuildEmoji、GuildEmoji类型
  execute: async (oldEmoji, newEmoji) => {
    console.log(`表情更新:${oldEmoji.name} -> ${newEmoji.name}`);
  }
});
类写法

如果你坚持用类实现,只要给IBotEvent传入对应事件名的泛型参数即可:

import type { IBotEvent } from "../interfaces";
import type { ClientEvents } from "discord.js";

export default class ReadyEvent implements IBotEvent<"ready"> {
  name = "ready" as const;
  once = true as const;

  // 参数自动推导为Client<true>类型
  async execute(client) {
    console.log(`登录成功,账号:${client.user.tag}`);
  }
}

3. 调整主文件注册逻辑

主文件的注册逻辑只需要微调,因为我们的接口定义已经保证了event.execute的参数和事件名对应的参数完全匹配,所以这里的any[]是类型安全的:

const eventFiles = fs
  .readdirSync("./events")
  .filter((file) => file.endsWith(".ts"));

eventFiles.forEach(async (file) => {
  // 如果是用工厂函数导出的字面量,直接取default即可;如果是类导出,需要new一下
  const event = (await import(`./events/${file}`)).default as IBotEvent;
  if (event.once) {
    this.once(event.name, (...args: any[]) => event.execute(...args));
  } else {
    this.on(event.name, (...args: any[]) => event.execute(...args));
  }
});

实现原理

核心是利用TS的泛型约束和元组映射:将IBotEvent的泛型参数K约束为ClientEvents的键,execute的参数直接映射为ClientEvents[K]对应的元组类型,TS会自动完成类型推导和校验,不需要手动复制参数定义。


内容的提问来源于stack exchange,提问作者Legiit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 05:54:05