Command Handling With TypeScript

Hello, I'm having trouble setting up command handling with TypeScript. I'm trying to follow the official guide that is directed towards JavaScript. What I Have So Far /src/index.ts
// Dependencies
import { Client, Events, GatewayIntentBits } from 'discord.js';

// Create Client Instance
const client = new Client({
intents: [
GatewayIntentBits.Guilds
]
});

// Log Ready Event
client.once(Events.ClientReady, (readyClient: Client<true>) => {
console.group("[🟢] Client Ready");

console.info(`Username: "${readyClient.user.username}"`);
console.info(`Guilds: ${readyClient.guilds.cache.size}`);
console.info(`Users: ${readyClient.users.cache.size}`);

console.groupEnd();
});

// Connect Client
client.login(process.env.DISCORD_BOT_TOKEN);
// Dependencies
import { Client, Events, GatewayIntentBits } from 'discord.js';

// Create Client Instance
const client = new Client({
intents: [
GatewayIntentBits.Guilds
]
});

// Log Ready Event
client.once(Events.ClientReady, (readyClient: Client<true>) => {
console.group("[🟢] Client Ready");

console.info(`Username: "${readyClient.user.username}"`);
console.info(`Guilds: ${readyClient.guilds.cache.size}`);
console.info(`Users: ${readyClient.users.cache.size}`);

console.groupEnd();
});

// Connect Client
client.login(process.env.DISCORD_BOT_TOKEN);
/src/commands/utility/ping.ts
// Dependencies
import { CommandInteraction, SlashCommandBuilder } from 'discord.js';

// Export Command
module.exports = {
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Ping the app."),
async execute(interaction: CommandInteraction) {
await interaction.reply("Pong!");
},
};
// Dependencies
import { CommandInteraction, SlashCommandBuilder } from 'discord.js';

// Export Command
module.exports = {
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Ping the app."),
async execute(interaction: CommandInteraction) {
await interaction.reply("Pong!");
},
};
Where I Am Stuck
We recommend attaching a .commands property to your client instance so that you can access your commands in other files. The rest of the examples in this guide will follow this convention. For TypeScript users, we recommend extending the base Client class to add this property, casting, or augmenting the module type.
I'd like to ask if what I currently have is good so far, and where I go from here. I already tried a couple of different things over the last few days, but I'm starting over and immediately asking here to be sure I understand what I'm doing.
6 Replies
d.js toolkit
d.js toolkit•2mo ago
- What's your exact discord.js npm list discord.js and node node -v version? - Not a discord.js issue? Check out #other-js-ts. - Consider reading #how-to-get-help to improve your question! - Explain what exactly your issue is. - Post the full error stack trace, not just the top part! - Show your code! - Issue solved? Press the button!
0fficerSally
0fficerSallyOP•2mo ago
- Node.js: v20.15.1 - discord.js: v14.16.3 Okay, thank you! I explicitly typed the readyClient parameter, because otherwise it'd get marked as an error for me, probably due to the way I have set up my tsconfig.json. I'm just not sure how to attach .commands to my client instance. To extend the base Client class, I don't know whether I should augment it or cast it.
d.js docs
d.js docs•2mo ago
We highly recommend you extend the Client structure properly instead of just attaching custom properties like .commands to the regular discord.js Client instance. - Using typescript, you might want to consider casting or augmenting the module type
Mark
Mark•2mo ago
You could also look at how create-discord-bot handles it, the commands are not bound to the client
HoeenHero
HoeenHero•2mo ago
I personally ended up just exporting a few Collections as my command/event registries:
/**
* Command Interface - Represents a command's data and response method.
* data: A Discord.SlashCommandBuilder object that has been used to build the command's data.
* execute: The method that executes when the command is called. Provides the interaction that
* triggerd the command as an argument.
* autocomplete: An optional method that executes to provide autocomplete options to the user.
*/
export interface ICommand {
data: Discord.SlashCommandOptionsOnlyBuilder;
execute: (interaction: Discord.ChatInputCommandInteraction) => Promise<void>;
autocomplete?: (interaction: Discord.AutocompleteInteraction) => Promise<void>;
}

/**
* All commands are internally registered here.
*/
export const commands = new Discord.Collection<string, ICommand>();
/**
* Command Interface - Represents a command's data and response method.
* data: A Discord.SlashCommandBuilder object that has been used to build the command's data.
* execute: The method that executes when the command is called. Provides the interaction that
* triggerd the command as an argument.
* autocomplete: An optional method that executes to provide autocomplete options to the user.
*/
export interface ICommand {
data: Discord.SlashCommandOptionsOnlyBuilder;
execute: (interaction: Discord.ChatInputCommandInteraction) => Promise<void>;
autocomplete?: (interaction: Discord.AutocompleteInteraction) => Promise<void>;
}

/**
* All commands are internally registered here.
*/
export const commands = new Discord.Collection<string, ICommand>();
I can access them in any file I need to with
import { commands } from './common'; // relative file location may vary
import { commands } from './common'; // relative file location may vary
0fficerSally
0fficerSallyOP•2mo ago
Thank you for all of your suggestions! I have to put my project on hold for now, but I will definitely continue here once I have the opportunity. I will definitely document my progress here once I figured it out, perhaps it can help someone else in the future.
Want results from more Discord servers?
Add your server