Broken TypeScript command (from lack of knowledge of TS)

I'm attempting to create a command following the Sapphire wiki, but it seems that I can't get the command to work at all. Note: I am very new to Typescript, I am not familiar with the syntax or the proper form to do stuff, please be gentle
import {Subcommand} from '@sapphire/plugin-subcommands';
import {Args} from "@sapphire/framework";
import {Message} from "discord.js";

export class AccountCommand extends Subcommand {
public constructor(context: Subcommand.Context, options: Subcommand.Options) {
super(context, {
...options,
name: 'account',
aliases: ['acc', 'link', 'verify'],
description: 'Link to your Discord Account',
subcommands: [
{
name: 'link',
messageRun: 'messageLink',
chatInputRun: 'chatInputLink',
default: true
},
{
name: 'list',
messageRun: 'messageList',
chatInputRun: 'chatInputList'
},
{
name: 'unlink',
messageRun: 'messageUnlink',
chatInputRun: 'chatInputUnlink',
}
]
});
}

registerApplicationCommands(registry: Subcommand.Registry) {
registry.registerChatInputCommand((builder) =>
builder
.setName(this.name)
.setDescription(this.description)
.addSubcommand((command) =>
command
.setName('link')
.setDescription('Link your Discord Account')
.addStringOption((option) =>
option.setName('summoner').setDescription('Summoner Name').setRequired(true)
)
)
.addSubcommand((command) =>
command
.setName('list')
.setDescription('View summoner names linked to your Discord Account')
)
.addSubcommand((command) =>
command
.setName('unlink')
.setDescription('Unlink a summoner name from your Discord Account')
.addStringOption((option) =>
option.setName('summoner').setDescription('Summoner Name').setRequired(true)
)
)
);
}

/** START Link Subcommand **/
public async messageLink(message: Message, args: Args) {
return message.channel.send('Account Link')
}

public async chatInputLink(interaction: Subcommand.ChatInputCommandInteraction) {
return interaction.reply('Account Link Interaction');
}
/** END link Subcommand **/

/** START List Subcommand **/
public async messageList(message: Message, args: Args) {
return message.channel.send('Account List')
}

public async chatInputList(interaction: Subcommand.ChatInputCommandInteraction) {
return interaction.reply('Account List Interaction');
}
/** END List Subcommand **/

/** START Unlink Subcommand **/
public async messageUnlink(message: Message, args: Args) {
return message.channel.send('Account Unlink')
}

public async chatInputUnlink(interaction: Subcommand.ChatInputCommandInteraction) {
return interaction.reply('Account Unlink Interaction');
}
/** END Unlink Subcommand **/
}
import {Subcommand} from '@sapphire/plugin-subcommands';
import {Args} from "@sapphire/framework";
import {Message} from "discord.js";

export class AccountCommand extends Subcommand {
public constructor(context: Subcommand.Context, options: Subcommand.Options) {
super(context, {
...options,
name: 'account',
aliases: ['acc', 'link', 'verify'],
description: 'Link to your Discord Account',
subcommands: [
{
name: 'link',
messageRun: 'messageLink',
chatInputRun: 'chatInputLink',
default: true
},
{
name: 'list',
messageRun: 'messageList',
chatInputRun: 'chatInputList'
},
{
name: 'unlink',
messageRun: 'messageUnlink',
chatInputRun: 'chatInputUnlink',
}
]
});
}

registerApplicationCommands(registry: Subcommand.Registry) {
registry.registerChatInputCommand((builder) =>
builder
.setName(this.name)
.setDescription(this.description)
.addSubcommand((command) =>
command
.setName('link')
.setDescription('Link your Discord Account')
.addStringOption((option) =>
option.setName('summoner').setDescription('Summoner Name').setRequired(true)
)
)
.addSubcommand((command) =>
command
.setName('list')
.setDescription('View summoner names linked to your Discord Account')
)
.addSubcommand((command) =>
command
.setName('unlink')
.setDescription('Unlink a summoner name from your Discord Account')
.addStringOption((option) =>
option.setName('summoner').setDescription('Summoner Name').setRequired(true)
)
)
);
}

/** START Link Subcommand **/
public async messageLink(message: Message, args: Args) {
return message.channel.send('Account Link')
}

public async chatInputLink(interaction: Subcommand.ChatInputCommandInteraction) {
return interaction.reply('Account Link Interaction');
}
/** END link Subcommand **/

/** START List Subcommand **/
public async messageList(message: Message, args: Args) {
return message.channel.send('Account List')
}

public async chatInputList(interaction: Subcommand.ChatInputCommandInteraction) {
return interaction.reply('Account List Interaction');
}
/** END List Subcommand **/

/** START Unlink Subcommand **/
public async messageUnlink(message: Message, args: Args) {
return message.channel.send('Account Unlink')
}

public async chatInputUnlink(interaction: Subcommand.ChatInputCommandInteraction) {
return interaction.reply('Account Unlink Interaction');
}
/** END Unlink Subcommand **/
}
This is the command I created, the .js version worked, but when i moved it over to TypeScript it just stopped working entirely.
Solution:
My advice Baylem, use @sapphire/cli to generate a bot
Jump to solution
36 Replies
Favna
Favna•10mo ago
Please share your full package.json, tsconfig.json and file/folder tree
Lioness100
Lioness100•10mo ago
What isn't working? Do you have a stack trace/error? Was it a build error?
Baylem
Baylem•10mo ago
https://i.imgur.com/iGxWViy.png
{
"name": "rigel-js",
"version": "1.0.0",
"description": "",
"author": "Baylem",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"dependencies": {
"@mikro-orm/core": "^5.7.14",
"@mikro-orm/mongodb": "^5.7.14",
"@sapphire/framework": "^4.5.3",
"@sapphire/plugin-subcommands": "^4.2.0",
"@sapphire/utilities": "^3.13.0",
"discord.js": "^14.13.0"
}
}
{
"name": "rigel-js",
"version": "1.0.0",
"description": "",
"author": "Baylem",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"dependencies": {
"@mikro-orm/core": "^5.7.14",
"@mikro-orm/mongodb": "^5.7.14",
"@sapphire/framework": "^4.5.3",
"@sapphire/plugin-subcommands": "^4.2.0",
"@sapphire/utilities": "^3.13.0",
"discord.js": "^14.13.0"
}
}
Imgur
Baylem
Baylem•10mo ago
The .js command is registering correctly and the bot is responding, the ts one is not showing any errors, no build errors and the bot is not reacting at all either probably my tsconfig is the issue:
{
"extends": "@lioness100/configs/tsconfig",
"compilerOptions": {
"rootDir": "src",
"baseUrl": "src"
},
"resolveJsonModule": true,
"include": ["src"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
}
{
"extends": "@lioness100/configs/tsconfig",
"compilerOptions": {
"rootDir": "src",
"baseUrl": "src"
},
"resolveJsonModule": true,
"include": ["src"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
}
Like I said I'm new to it, so I apologize if this is just lack of knowledge
Lioness100
Lioness100•10mo ago
Haha is that my tsconfig
Baylem
Baylem•10mo ago
Yeah I figured why not copy the person that knows way more than me lol
Lioness100
Lioness100•10mo ago
From the screenshot it looks like the JSON files are in the src/ folder? Is that correct?
Baylem
Baylem•10mo ago
yes oh wait no no the package and tsconfig are not in src
Lioness100
Lioness100•10mo ago
Is there a commands/account.js file in the dist folder?
Baylem
Baylem•10mo ago
yes I believe so one moment, I'm in a game I'll screenshot when I get back I can still chat though
Favna
Favna•10mo ago
you need yo compile your account.ts to JS then change main to dist/index.js and rewrite all your src files from TS to JS the ncompile then run the bot again
Baylem
Baylem•10mo ago
oh wait is it because i did main/index.js not dist/ im sad ok
Favna
Favna•10mo ago
also that tsconfig is invalid
Lioness100
Lioness100•10mo ago
Yeah most of those options should be under compilerOptions
Favna
Favna•10mo ago
{
"extends": "@lioness100/configs/tsconfig",
"compilerOptions": {
"rootDir": "src",
"baseUrl": "src"
"outDir": "dist",
"resolveJsonModule": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
},
"include": ["src"],
}
{
"extends": "@lioness100/configs/tsconfig",
"compilerOptions": {
"rootDir": "src",
"baseUrl": "src"
"outDir": "dist",
"resolveJsonModule": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
},
"include": ["src"],
}
it should be like this
Baylem
Baylem•10mo ago
it didnt throw errors when compiling which is weird okay thanks
Lioness100
Lioness100•10mo ago
A lot of them are also already enabled in the config you're extending iirc
Baylem
Baylem•10mo ago
ill mess around with it this seems to just be a lack of knowledge thing thank you
Favna
Favna•10mo ago
Lioness idk your config but there is a good chance you need to update it btw if you didnt already, for TS 5.2
Favna
Favna•10mo ago
GitHub
Update the tsconfig to better support TypeScript 5.2 by favna · Pul...
Warning needs rebase & merge to ensure that the breaking change doesn't show up for the other packages As TS 5.2 is really pushing for separating CJS and ESM entirely I have decided to com...
Lioness100
Lioness100•10mo ago
I'll look into it
Favna
Favna•10mo ago
TL;DR node16 for both module and moduleResolution and make verbatimModuleSyntax opt-in
Lioness100
Lioness100•10mo ago
But also @Baylem it's not in package.json so I'm not sure if typescript is even reading the extended config
Solution
Favna
Favna•10mo ago
My advice Baylem, use @sapphire/cli to generate a bot
Favna
Favna•10mo ago
then you'll have a good setup to start from (just give it like an hour so we can roll out updates like this PR)
Baylem
Baylem•10mo ago
okay
Lioness100
Lioness100•10mo ago
Damn diss my config like that 🙄
Favna
Favna•10mo ago
I mean it's not just that. It's also the main property, package.json, folder setup, etc
Lioness100
Lioness100•10mo ago
Oh so it's not just that Wow hide your disdain for me next time
Favna
Favna•10mo ago
lmao
Baylem
Baylem•10mo ago
now now ladies you're both beautiful
Favna
Favna•10mo ago
only lioness is a lady but thanks
Baylem
Baylem•10mo ago
error TS5109: Option 'moduleResolution' must be set to 'Node16' (or left unspecified) when option 'module' is set to 'Node16'. after using CLI, I'm troubleshooting and trying things. I tried adding the Compiler options above but I'm gettign this issue: https://discord.com/channels/737141877803057244/1145844807017693295 I tried manually setting those to Node16 as stated here - and I get a top level export error, as stated above A top-level  export  modifier cannot be used on value declarations in a CommonJS module when  verbatimModuleSyntax  is enabled.
Favna
Favna•10mo ago
Either write a full ESM bot or disable verbatimModuleSyntax
Baylem
Baylem•10mo ago
I used Sapphire CLI to generate the bot. I have 0 code written by me, so the templates arent ESM till the next update/release? @Favna so adding verbatimModuleSyntax: false to my compiler options should work? ah ok nvm it was a TS version mismatch as posted in the other thread
Favna
Favna•10mo ago
@Baylem update has been released, see #Announcements