1 year ago

#327294

test-img

DeMineArchiver

TypeError: "method is not a function" error when downcasting to a subclass - discrord.js

Please, don't mind my grammar and code mistakes because I am a self-taught coder

I am developing a multi-purpose Discord bot with NodeJS using discord.js and typescript libraries.

When creating the ticket system, and trying to make it as universal as possible, I ran into a problem with downcasting subclasses. To create a ticket text channel, I use CategoryChannel.createChannel() method, which returns Promise<GuildChannel>, in my case, I set the CategoryCreateChannelOptions.type to "GUILD_TEXT", so the method returns Promise<TextChannel>.

After creating a channel, I want to add custom properties to it, such as tickedID: string (a custom random UID I generate for each ticket), ticketGroup: string (a group which the ticket belongs to, in order to differentiate between tickets from different categories, for example: support, suggestion, warning, form ticket categories) and channelID: string (ID of the channel the ticket is).

To do that, I have created a Ticket class, which extends TextChannel.

Here's a cleanified version of the Ticket class code:

// Imports
import { Guild, Message, MessageOptions, TextChannel } from "discord.js";
import { RawGuildChannelData } from "discord.js/typings/rawDataTypes";
import { TicketSetupFunctionOptions, TicketType } from "../Types/Ticket";
import { BetterClient } from "./Client";

// Ticket class
export class Ticket extends TextChannel implements TicketType{
    public ticketID: string;
    public ticketGroup: string;
    public channelContent: MessageOptions[];
    
    public constructor(options: TicketType, guild: Guild, data?: RawGuildChannelData, client?: BetterClient, immediatePatch?: boolean) {
        super(guild, data, client, immediatePatch);
        Object.assign(this, options);
    }

    // Setup method, the one which is the main problem
    public async setup<TicketSetupFunction>(options: TicketSetupFunctionOptions): Promise<Ticket> {
        // Do the code (it have never got to point of executing the code so there's no reason to put it here)
        
        return new Promise<Ticket>((resolve, reject) => {
            resolve(this);
        });
        
    }
}

I even tried putting the setup() method into the type, but it didn't help. Type TicketType:

// Imports
import { MessageOptions, TextChannel } from "discord.js";
import { Ticket } from "../Structures/Ticket";

// Types
export interface TicketSetupFunctionOptions {
    channelContent: MessageOptions[];
}

export type TicketSetupFunction = (TicketSetupFunctionOptions) => Promise<Ticket>;

export type TicketType = {
    ticketID: string;
    ticketGroup: string;
    channelContent?: MessageOptions[];
    setup: TicketSetupFunction;
} & TextChannel;

In my TicketManager class (it is basically the tickets system) I have a TicketManager.createTicket() method, which takes a CategoryChannelResolvable and OverwriteResolvable[] as its arguments, which are then used in CategoryChannel.createChannel() method. It returns Promise<TextChannel>, but I need downcast that into returning Ticket if the promise resolves. Here's the TicketManager.createChannel() code:

public async createChannel(category: CategoryChannel, permissions: OverwriteResolvable[]): Promise<Ticket> {
        const channel = await category.createChannel("ticket-name", {
            type: "GUILD_TEXT",
            permissionOverwrites: permissions
        }) as Ticket;

        return new Promise<Ticket>((resolve, reject) => resolve(channel));
    }

TicketManager.createTicket() is used to create, setup and save a ticket to the database. Inside of this method I resolve the category and generate a UID for the ticket.

After that, I call TicketManager.createChannel() and do the following:

public async createTicket(categoryChannel: CategoryChannelResolvable): Promise<Ticket> {
        const ticketID = generateUID(16);
        const category = await this.client.guilds.resolve(this.guild).channels.resolve(categoryChannel) as CategoryChannel;

        const ticket: Ticket = await this.createChannel(category);
        await ticket.setup({
            channelContent: this.defaultTicket.channelContent
        });

        return new Promise<Ticket>((resolve, reject) => resolve(ticket));
    }

After trying to create a ticket, the program gives this error:

Bot\src\Structures\TicketManager.ts:47
        await ticket.setup({ channelContent: this.defaultTicket.channelContent });
                     ^
TypeError: ticket.setup is not a function
    at TicketManager.createTicket (Bot\src\Structures\TicketManager.ts:47:22)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Event.run (Bot\src\Events\guildMemberAdd.ts:27:32)

The problem is that the method is perfectly typed and it even shows up in the autocomplete menu, but it always throws (no matter what I try to do) that error above.

I really hope somebody could help me figure this out, since it is a very important point in my bot development.

javascript

node.js

typescript

discord.js

downcast

0 Answers

Your Answer

Accepted video resources