[CLOSED] A question regarding discord.js with types. (TypeScript)

Hi! I'd have a question for the typescript users here. My commands can be run by the InteractionCreate and messageCreate event as well. Let me try to explain it. Since they can be a normal text message(with a prefix) or a slash command as well I have to get the arguments the following way (using plain javascript):
js async execute({ inter, args }) {
const testArg = inter.options?.getString('testarg', true) || args[0];
js async execute({ inter, args }) {
const testArg = inter.options?.getString('testarg', true) || args[0];
(This is because inter.options is not available when the interaction is a normal message) Now with typescript: I destruct the arguments this way since they can be of type CommandInteraction (when using InteractionCreate) and of type Message(when using messageCreate)
ts async execute({ inter, args }: { inter: CommandInteraction | Message, args: string[] }) { 
ts async execute({ inter, args }: { inter: CommandInteraction | Message, args: string[] }) { 
Now I can't get the argument the same way since inter.options only exists on CommandInteraction and not Message as well. So I have to check if the interaction is instanceof CommandInteraction just to get the options property.
ts (inter instanceof CommandInteraction) ? inter.options.getString('testarg', true) : args[0];
ts (inter instanceof CommandInteraction) ? inter.options.getString('testarg', true) : args[0];
"options" is declared this way in the discord.js module:
ts public options: Omit<     CommandInteractionOptionResolver<Cached>,     // ...     | 'getString'     | 'getChannel'     | 'getBoolean'   >;
//CommandInteractionOptionResolver looks like this
export class CommandInteractionOptionResolver<Cached extends CacheType = CacheType> { // ...   public getString(name: string, required: true): string;   public getString(name: string, required?: boolean): string | null; // ...
ts public options: Omit<     CommandInteractionOptionResolver<Cached>,     // ...     | 'getString'     | 'getChannel'     | 'getBoolean'   >;
//CommandInteractionOptionResolver looks like this
export class CommandInteractionOptionResolver<Cached extends CacheType = CacheType> { // ...   public getString(name: string, required: true): string;   public getString(name: string, required?: boolean): string | null; // ...
Now typescript tells me that Property 'getString' does not exist on type 'Omit<CommandInteractionOptionResolver<CacheType>, "getMessage" | "getFocused" | "getMentionable" | "getRole" | "getAttachment" | ... 6 more ... | "getSubcommand">' So I tried casting the inter.options property to type CommandInteractionOptionResolver like this which works fine:
ts (<CommandInteractionOptionResolver>inter.options).getString('testarg', false)
ts (<CommandInteractionOptionResolver>inter.options).getString('testarg', false)
And you might already see where this goes.. Just getting an argument (while also handling normal and slash commands at the same time) the code looks like this:
ts const testArg = (inter instanceof CommandInteraction) ? (<CommandInteractionOptionResolver>inter.options).getString('testarg', false) : args?[0];
ts const testArg = (inter instanceof CommandInteraction) ? (<CommandInteractionOptionResolver>inter.options).getString('testarg', false) : args?[0];
Here comes my question. Is there any other way of handling this so I can avoid all this work without setting the interaction to type any ? async execute({ inter, args }: { inter: any, args: string[] }) { Many thanks!
7 Replies
d.js toolkit
d.js toolkit12mo 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!
duck
duck12mo ago
CommandInteraction is for all application commands, including context menu commands if you're just looking for slash commands, you'd want ChatInputCommandInteraction you can use the typeguard <Interaction>.isChatInputCommand() rather than instanceof
Yuuki | ユウキ
Thank you for the quick response! I see. Using ChatInputCommandInteraction as well as the typeguard you mentioned is definitely a better approach. Do you have any ideas how could I avoid all these type casts or at least reduce them?
const testarg = (<ChatInputCommandInteraction>inter).isChatInputCommand() ? (<ChatInputCommandInteraction>inter).options.getString('testarg', true) : args[0];
const testarg = (<ChatInputCommandInteraction>inter).isChatInputCommand() ? (<ChatInputCommandInteraction>inter).options.getString('testarg', true) : args[0];
Also, any ideas on how should I handle the inter.member property the correct way? (Let's take an embed's setAuthor function for example.
.setAuthor({ name: inter.member?.user.username as string, iconURL: (inter.member?.user as User).avatarURL() as string })
.setAuthor({ name: inter.member?.user.username as string, iconURL: (inter.member?.user as User).avatarURL() as string })
(Attached some images of type problems regarding this issue)
Squid
Squid12mo ago
For the member, use the typeguard inter.inCachedGuild() which ensures that inter.member is a full GuildMember instance instead of null or an APIGuildMember object
Yuuki | ユウキ
Oh, great! Looks like I'm gonna have to do a little more research on typeguards now. Thanks! Hmm.. I could just do it like
const testarg = (<ChatInputCommandInteraction>inter).options.getString('testarg', true) || args[0];
const testarg = (<ChatInputCommandInteraction>inter).options.getString('testarg', true) || args[0];
instead of
const testarg = (<ChatInputCommandInteraction>inter).isChatInputCommand() ? (<ChatInputCommandInteraction>inter).options.getString('testarg', true) : args[0];
const testarg = (<ChatInputCommandInteraction>inter).isChatInputCommand() ? (<ChatInputCommandInteraction>inter).options.getString('testarg', true) : args[0];
is this a bad approach? lmao, since I'm now using the ChatInputCommandInteraction and not CommandInteraction I don't have to handle OptionResolvers like I did when I wrote the post, so you're right. This is the best solution handling them!
const testArg = (inter instanceof ChatInputCommandInteraction) ? inter.options.getString('testarg', true) : args[0];
const testArg = (inter instanceof ChatInputCommandInteraction) ? inter.options.getString('testarg', true) : args[0];
Many thanks!
Yuuki | ユウキ
Hmm, last question for now, any way I could improve this?
Yuuki | ユウキ
(I'm casting the avatar url to a string since the property "iconURL" in the setAuthor() fn only accepts a string | undefined and not string | null) Ahh nice! Forgot about the nullish coalescing op or whatever it is called. I also have to check if "inter" is an instance of ChatInputCommandInteraction to use the inChachedGuild typeguard right? Yea, well.. it is definitely a crazy idea to handle both type of commands in the block/file, but I didn't want to write separate code for each of them, now I can just set what type of the command is (normal, slash or both) and handle them at the same time. I also thought about merging the types but that would just make me more work.. as you said, replying would be some next level devilish thing pepeChrist how did I miss this lmao Before I switched to using typescript in this project I was using inter.member.user to get the user since the member property exists on both command types. But now I can just simply use (inter instanceof ChatInputCommandInteraction) ? (inter.user.id ) : inter.author.id; KEKW learned some new things though so thanks again! lovge