I am working on creating a discord bot in TypeScript. I wanted to create a generic command disbatcher and here is my work so far:
app.ts:
import * as Discord from 'discord.js';
import * as config from '../config'
import * as commands from './Commands/index'
const token : string = config.Token;
const _client = new Discord.Client();
_client.on('message', (msg) => {
let args : Array<string> = msg.content.split(' ')
let command : string = args.shift() || " ";
if(!command.startsWith("!")) return;
else{
commands[`${command.toLower().substring(1)}`]
}
})
Commands/Index.ts
export {default as ping} from './ping';
export {default as prong} from './prong';
Ping.ts : same structure for all commands
import { Message } from "discord.js";
export default {
name : 'ping',
description: 'Ping!',
execute(message: Message, args: Array<string>){
message.channel.send('Pong.');
}
}
When indexing the commands import I can successfuly call the right execute function using this:
commands['pong'].execute()
however, when trying to dynamically index it like this:
commands[command].execute()
I recieve the following error:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof import("c:/Users/alexs/Desktop/Discord Bot/src/Commands/index")'. No index signature with a parameter of type 'string' was found on type 'typeof import("c:/Users/alexs/Desktop/Discord Bot/src/Commands/index")'
Is there anyway I can typecast the command import as some kind of object or collection? If not, is there a way I could create some kind of accssesor to make this work? I am newer to typescript and am curious what is possible.
I suggest a different approach for your commands, this approach fixes 2 things:
Let's first create a interface for your commands, this interface describes the metadata, add as many as you want
export interface Command {
name: string
description: string
// Making `args` optional
execute(message: Message, args?: string[]) => any
}
Now that you have a shape for your command, let's make sure all your commands have the right shape
import { Command } from "./types"
// This will complain if you don't provide the right types for each property
const command: Command = {
name: "ping",
description: "Ping!",
execute(message: Message, args: string[]) => {
message.channel.send("Pong")
}
}
export = command
The next part is loading your commands, discord.js has glob as a dependency which can help you read files in a directory easily, let's use some utilities so we can have nice async / await usage
import glob from "glob" // included by discord.js
import { promisify } from "util" // Included by default
import { Command } from "./types"
// Make `glob` return a promise
const globPromise = promisify(glob)
const commands: Command = []
client.once("ready", async () => {
// Load all JavaScript / TypeScript files so it works properly after compiling
// Replace `test` with "await globPromise(`${__dirname}/commands/*.{.js,.ts}`)"
// I just did this to fix SO's syntax highlighting!
const commandFiles = test
for (const file of commandFiles) {
// I am not sure if this works, you could go for require(file) as well
const command = await import(file) as Command
commands.push(command)
}
})
const prefix = "!"
client.on("message", message => {
// Prevent the bot from replying to itself or other bots
if (message.author.bot) {
return
}
const [commandName, ...args] = message.content
.slice(prefix.length)
.split(/ +/)
const command = commands.find(c => c.name === commandName)
if (command) {
command.execute(message, args)
}
})
I hope this gives you some good starting point and shows you the power of TypeScript
You could import the type and then cast it like this
commands[command as Type].execute();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.