diff --git a/packages/discord.js/src/structures/Interaction.js b/packages/discord.js/src/structures/Interaction.js index 0422f6e0f..38e7330c6 100644 --- a/packages/discord.js/src/structures/Interaction.js +++ b/packages/discord.js/src/structures/Interaction.js @@ -186,20 +186,12 @@ class Interaction extends Base { return Boolean(this.guildId && !this.guild && this.member); } - /** - * Indicates whether this interaction is a {@link CommandInteraction}. - * @returns {boolean} - */ - isCommand() { - return this.type === InteractionType.ApplicationCommand; - } - /** * Indicates whether this interaction is a {@link ChatInputCommandInteraction}. * @returns {boolean} */ isChatInputCommand() { - return this.isCommand() && this.commandType === ApplicationCommandType.ChatInput; + return this.type === InteractionType.ApplicationCommand && this.commandType === ApplicationCommandType.ChatInput; } /** @@ -207,7 +199,10 @@ class Interaction extends Base { * @returns {boolean} */ isContextMenuCommand() { - return this.isCommand() && [ApplicationCommandType.User, ApplicationCommandType.Message].includes(this.commandType); + return ( + this.type === InteractionType.ApplicationCommand && + [ApplicationCommandType.User, ApplicationCommandType.Message].includes(this.commandType) + ); } /** @@ -226,36 +221,12 @@ class Interaction extends Base { return this.isContextMenuCommand() && this.commandType === ApplicationCommandType.Message; } - /** - * Indicates whether this interaction is a {@link ModalSubmitInteraction} - * @returns {boolean} - */ - isModalSubmit() { - return this.type === InteractionType.ModalSubmit; - } - - /** - * Indicates whether this interaction is an {@link AutocompleteInteraction} - * @returns {boolean} - */ - isAutocomplete() { - return this.type === InteractionType.ApplicationCommandAutocomplete; - } - - /** - * Indicates whether this interaction is a {@link MessageComponentInteraction}. - * @returns {boolean} - */ - isMessageComponent() { - return this.type === InteractionType.MessageComponent; - } - /** * Indicates whether this interaction is a {@link ButtonInteraction}. * @returns {boolean} */ isButton() { - return this.isMessageComponent() && this.componentType === ComponentType.Button; + return this.type === InteractionType.MessageComponent && this.componentType === ComponentType.Button; } /** @@ -263,7 +234,7 @@ class Interaction extends Base { * @returns {boolean} */ isSelectMenu() { - return this.isMessageComponent() && this.componentType === ComponentType.SelectMenu; + return this.type === InteractionType.MessageComponent && this.componentType === ComponentType.SelectMenu; } /** diff --git a/packages/discord.js/src/structures/ThreadChannel.js b/packages/discord.js/src/structures/ThreadChannel.js index edc31da86..bab761756 100644 --- a/packages/discord.js/src/structures/ThreadChannel.js +++ b/packages/discord.js/src/structures/ThreadChannel.js @@ -511,14 +511,6 @@ class ThreadChannel extends Channel { return this.archived && this.sendable && (!this.locked || this.manageable); } - /** - * Whether this thread is a private thread - * @returns {boolean} - */ - isPrivate() { - return this.type === ChannelType.GuildPrivateThread; - } - /** * Deletes this thread. * @param {string} [reason] Reason for deleting this thread diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 18a64c51b..1fb7b95da 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -412,6 +412,7 @@ export interface InteractionResponseFields export type BooleanCache = T extends 'cached' ? true : false; export abstract class CommandInteraction extends Interaction { + public type: InteractionType.ApplicationCommand; public get command(): ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; public options: Omit< CommandInteractionOptionResolver, @@ -564,6 +565,7 @@ export class BitField { export class ButtonInteraction extends MessageComponentInteraction { private constructor(client: Client, data: RawMessageButtonInteractionData); + public componentType: ComponentType.Button; public get component(): CacheTypeReducer< Cached, ButtonComponent, @@ -571,7 +573,6 @@ export class ButtonInteraction extends Mes ButtonComponent | APIButtonComponent, ButtonComponent | APIButtonComponent >; - public componentType: ComponentType.Button; public inGuild(): this is ButtonInteraction<'raw' | 'cached'>; public inCachedGuild(): this is ButtonInteraction<'cached'>; public inRawGuild(): this is ButtonInteraction<'raw'>; @@ -750,7 +751,7 @@ export abstract class Channel extends Base { public get url(): string; public delete(): Promise; public fetch(force?: boolean): Promise; - public isThread(): this is ThreadChannel; + public isThread(): this is AnyThreadChannel; public isTextBased(): this is TextBasedChannel; public isDMBased(): this is PartialGroupDMChannel | DMChannel | PartialDMChannel; public isVoiceBased(): this is VoiceBasedChannel; @@ -918,6 +919,7 @@ export abstract class Collector extends EventEmi } export class ChatInputCommandInteraction extends CommandInteraction { + public commandType: ApplicationCommandType.ChatInput; public options: Omit, 'getMessage' | 'getFocused'>; public inGuild(): this is ChatInputCommandInteraction<'raw' | 'cached'>; public inCachedGuild(): this is ChatInputCommandInteraction<'cached'>; @@ -926,6 +928,7 @@ export class ChatInputCommandInteraction e } export class AutocompleteInteraction extends Interaction { + public type: InteractionType.ApplicationCommandAutocomplete; public get command(): ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; public channelId: Snowflake; public commandId: Snowflake; @@ -1002,6 +1005,7 @@ export class CommandInteractionOptionResolver extends CommandInteraction { + public commandType: ApplicationCommandType.Message | ApplicationCommandType.User; public options: Omit< CommandInteractionOptionResolver, | 'getFocused' @@ -1465,6 +1469,15 @@ export type CacheTypeReducer< ? PresentType : Fallback; +export type AnyInteraction = + | ChatInputCommandInteraction + | MessageContextMenuCommandInteraction + | UserContextMenuCommandInteraction + | SelectMenuInteraction + | ButtonInteraction + | AutocompleteInteraction + | ModalSubmitInteraction; + export class Interaction extends Base { // This a technique used to brand different cached types. Or else we'll get `never` errors on typeguard checks. private readonly _cacheType: Cached; @@ -1495,16 +1508,13 @@ export class Interaction extends Base { public inCachedGuild(): this is Interaction<'cached'>; public inRawGuild(): this is Interaction<'raw'>; public isButton(): this is ButtonInteraction; - public isCommand(): this is CommandInteraction; public isChatInputCommand(): this is ChatInputCommandInteraction; public isContextMenuCommand(): this is ContextMenuCommandInteraction; public isMessageContextMenuCommand(): this is MessageContextMenuCommandInteraction; public isAutocomplete(): this is AutocompleteInteraction; public isUserContextMenuCommand(): this is UserContextMenuCommandInteraction; - public isMessageComponent(): this is MessageComponentInteraction; public isSelectMenu(): this is SelectMenuInteraction; public isRepliable(): this is this & InteractionResponseFields; - public isModalSubmit(): this is ModalSubmitInteraction; } export class InteractionCollector extends Collector]> { @@ -1667,7 +1677,7 @@ export class Message extends Base { public reactions: ReactionManager; public stickers: Collection; public system: boolean; - public get thread(): ThreadChannel | null; + public get thread(): AnyThreadChannel | null; public tts: boolean; public type: MessageType; public get url(): string; @@ -1694,7 +1704,7 @@ export class Message extends Base { public removeAttachments(): Promise; public reply(options: string | MessagePayload | ReplyMessageOptions): Promise; public resolveComponent(customId: string): MessageActionRowComponent | null; - public startThread(options: StartThreadOptions): Promise; + public startThread(options: StartThreadOptions): Promise; public suppressEmbeds(suppress?: boolean): Promise; public toJSON(): unknown; public toString(): string; @@ -1748,6 +1758,7 @@ export class MessageCollector extends Collector extends Interaction { protected constructor(client: Client, data: RawMessageComponentInteractionData); + public type: InteractionType.MessageComponent; public get component(): CacheTypeReducer< Cached, MessageActionRowComponent, @@ -1755,7 +1766,7 @@ export class MessageComponentInteraction e MessageActionRowComponent | Exclude>, MessageActionRowComponent | Exclude> >; - public componentType: Exclude; + public componentType: Exclude; public customId: string; public channelId: Snowflake; public deferred: boolean; @@ -1800,6 +1811,7 @@ export class MessageComponentInteraction e export class MessageContextMenuCommandInteraction< Cached extends CacheType = CacheType, > extends ContextMenuCommandInteraction { + public commandType: ApplicationCommandType.Message; public get targetMessage(): NonNullable['message']>; public inGuild(): this is MessageContextMenuCommandInteraction<'raw' | 'cached'>; public inCachedGuild(): this is MessageContextMenuCommandInteraction<'cached'>; @@ -1934,6 +1946,7 @@ export interface ModalMessageModalSubmitInteraction extends Interaction { private constructor(client: Client, data: APIModalSubmitInteraction); + public type: InteractionType.ModalSubmit; public readonly customId: string; public readonly components: ActionRowModalData[]; public readonly fields: ModalSubmitFields; @@ -2439,6 +2452,18 @@ export class TextChannel extends BaseGuildTextChannel { public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise; } +export type AnyThreadChannel = PublicThreadChannel | PrivateThreadChannel; + +export interface PublicThreadChannel extends ThreadChannel { + type: ChannelType.GuildPublicThread | ChannelType.GuildNewsThread; +} + +export interface PrivateThreadChannel extends ThreadChannel { + get createdTimestamp(): number; + get createdAt(): Date; + type: ChannelType.GuildPrivateThread; +} + export class ThreadChannel extends TextBasedChannelMixin(Channel, ['fetchWebhooks', 'createWebhook']) { private constructor(guild: Guild, data?: RawThreadChannelData, client?: Client, fromInteraction?: boolean); public archived: boolean | null; @@ -2469,15 +2494,10 @@ export class ThreadChannel extends TextBasedChannelMixin(Channel, ['fetchWebhook public rateLimitPerUser: number | null; public type: ThreadChannelType; public get unarchivable(): boolean; - public isPrivate(): this is this & { - get createdTimestamp(): number; - get createdAt(): Date; - type: ChannelType.GuildPrivateThread; - }; public delete(reason?: string): Promise; - public edit(data: ThreadEditData, reason?: string): Promise; - public join(): Promise; - public leave(): Promise; + public edit(data: ThreadEditData, reason?: string): Promise; + public join(): Promise; + public leave(): Promise; public permissionsFor(memberOrRole: GuildMember | Role, checkAdmin?: boolean): Readonly; public permissionsFor( memberOrRole: GuildMemberResolvable | RoleResolvable, @@ -2485,15 +2505,15 @@ export class ThreadChannel extends TextBasedChannelMixin(Channel, ['fetchWebhook ): Readonly | null; public fetchOwner(options?: BaseFetchOptions): Promise; public fetchStarterMessage(options?: BaseFetchOptions): Promise; - public setArchived(archived?: boolean, reason?: string): Promise; + public setArchived(archived?: boolean, reason?: string): Promise; public setAutoArchiveDuration( autoArchiveDuration: ThreadAutoArchiveDuration, reason?: string, - ): Promise; - public setInvitable(invitable?: boolean, reason?: string): Promise; - public setLocked(locked?: boolean, reason?: string): Promise; - public setName(name: string, reason?: string): Promise; - public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise; + ): Promise; + public setInvitable(invitable?: boolean, reason?: string): Promise; + public setLocked(locked?: boolean, reason?: string): Promise; + public setName(name: string, reason?: string): Promise; + public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise; public toString(): ChannelMention; } @@ -2505,7 +2525,7 @@ export class ThreadMember extends Base { public get joinedAt(): Date | null; public joinedTimestamp: number | null; public get manageable(): boolean; - public thread: ThreadChannel; + public thread: AnyThreadChannel; public get user(): User | null; public get partial(): false; public remove(reason?: string): Promise; @@ -2566,6 +2586,7 @@ export class User extends PartialTextBasedChannel(Base) { export class UserContextMenuCommandInteraction< Cached extends CacheType = CacheType, > extends ContextMenuCommandInteraction { + public commandType: ApplicationCommandType.User; public get targetUser(): User; public get targetMember(): CacheTypeReducer; public inGuild(): this is UserContextMenuCommandInteraction<'raw' | 'cached'>; @@ -3371,8 +3392,8 @@ export class StageInstanceManager extends CachedManager extends CachedManager { private constructor(channel: TextChannel | NewsChannel, iterable?: Iterable); public channel: TextChannel | NewsChannel; - public create(options: ThreadCreateOptions): Promise; - public fetch(options: ThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise; + public create(options: ThreadCreateOptions): Promise; + public fetch(options: ThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise; public fetch(options?: FetchThreadsOptions, cacheOptions?: { cache?: boolean }): Promise; public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise; public fetchActive(cache?: boolean): Promise; @@ -3380,7 +3401,7 @@ export class ThreadManager extends CachedManager { private constructor(thread: ThreadChannel, iterable?: Iterable); - public thread: ThreadChannel; + public thread: AnyThreadChannel; public get me(): ThreadMember | null; public add(member: UserResolvable | '@me', reason?: string): Promise; public fetch(options?: ThreadMemberFetchOptions): Promise; @@ -3886,21 +3907,21 @@ export interface ClientEvents { roleCreate: [role: Role]; roleDelete: [role: Role]; roleUpdate: [oldRole: Role, newRole: Role]; - threadCreate: [thread: ThreadChannel, newlyCreated: boolean]; - threadDelete: [thread: ThreadChannel]; - threadListSync: [threads: Collection, guild: Guild]; + threadCreate: [thread: AnyThreadChannel, newlyCreated: boolean]; + threadDelete: [thread: AnyThreadChannel]; + threadListSync: [threads: Collection, guild: Guild]; threadMemberUpdate: [oldMember: ThreadMember, newMember: ThreadMember]; threadMembersUpdate: [ addedMembers: Collection, removedMembers: Collection, - thread: ThreadChannel, + thread: AnyThreadChannel, ]; - threadUpdate: [oldThread: ThreadChannel, newThread: ThreadChannel]; + threadUpdate: [oldThread: AnyThreadChannel, newThread: AnyThreadChannel]; typingStart: [typing: Typing]; userUpdate: [oldUser: User | PartialUser, newUser: User]; voiceStateUpdate: [oldState: VoiceState, newState: VoiceState]; webhookUpdate: [channel: TextChannel | NewsChannel | VoiceChannel]; - interactionCreate: [interaction: Interaction]; + interactionCreate: [interaction: AnyInteraction]; shardDisconnect: [closeEvent: CloseEvent, shardId: number]; shardError: [error: Error, shardId: number]; shardReady: [shardId: number, unavailableGuilds: Set | undefined]; @@ -4222,7 +4243,7 @@ export interface FetchChannelOptions extends BaseFetchOptions { } export interface FetchedThreads { - threads: Collection; + threads: Collection; hasMore?: boolean; } @@ -4398,7 +4419,7 @@ export interface GuildAuditLogsEntryTargetField; @@ -5152,7 +5173,7 @@ export type GuildBasedChannel = Extract; export type NonCategoryGuildBasedChannel = Exclude; -export type NonThreadGuildBasedChannel = Exclude; +export type NonThreadGuildBasedChannel = Exclude; export type GuildTextBasedChannel = Extract; @@ -5162,7 +5183,7 @@ export type TextBasedChannelResolvable = Snowflake | TextBasedChannel; export type ThreadAutoArchiveDuration = 60 | 1440 | 4320 | 10080; -export type ThreadChannelResolvable = ThreadChannel | Snowflake; +export type ThreadChannelResolvable = AnyThreadChannel | Snowflake; export type ThreadChannelType = | ChannelType.GuildNewsThread diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 438eee9f5..c4dbfbd03 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -21,6 +21,7 @@ import { TextInputStyle, APITextInputComponent, APIEmbed, + ApplicationCommandType, } from 'discord-api-types/v10'; import { ApplicationCommand, @@ -94,7 +95,6 @@ import { GuildAuditLogsEntry, GuildAuditLogs, StageInstance, - PartialDMChannel, ActionRowBuilder, ButtonComponent, SelectMenuComponent, @@ -123,6 +123,9 @@ import { UserMention, PartialGroupDMChannel, Attachment, + MessageContextMenuCommandInteraction, + UserContextMenuCommandInteraction, + AnyThreadChannel, } from '.'; import { expectAssignable, expectDeprecated, expectNotAssignable, expectNotType, expectType } from 'tsd'; import { UnsafeButtonBuilder, UnsafeEmbedBuilder, UnsafeSelectMenuBuilder } from '@discordjs/builders'; @@ -802,10 +805,20 @@ client.on('messageCreate', async message => { channel.send({ components: [row, buttonsRow, selectsRow], embeds: [embed, buildersEmbed, embedData] }); }); +client.on('threadCreate', thread => { + if (thread.type === ChannelType.GuildPrivateThread) { + expectType(thread.createdTimestamp); + expectType(thread.createdAt); + } else { + expectType(thread.createdTimestamp); + expectType(thread.createdAt); + } +}); + client.on('threadMembersUpdate', (addedMembers, removedMembers, thread) => { expectType>(addedMembers); expectType>(removedMembers); - expectType(thread); + expectType(thread); const left = removedMembers.first(); if (!left) return; @@ -823,7 +836,11 @@ client.on('interactionCreate', async interaction => { expectType(interaction.channelId); expectType(interaction.member); - if (!interaction.isCommand()) return; + if (interaction.type === InteractionType.MessageComponent) { + expectType(interaction.channelId); + } + + if (interaction.type !== InteractionType.ApplicationCommand) return; void new ActionRowBuilder(); @@ -862,10 +879,6 @@ client.on('interactionCreate', async interaction => { }, ], }); - - if (interaction.isMessageComponent()) { - expectType(interaction.channelId); - } }); client.login('absolutely-valid-token'); @@ -1153,6 +1166,31 @@ client.on('interactionCreate', interaction => { }); client.on('interactionCreate', async interaction => { + if (interaction.type === InteractionType.MessageComponent) { + expectType(interaction); + expectType(interaction.component); + expectType(interaction.message); + if (interaction.inCachedGuild()) { + expectAssignable(interaction); + expectType(interaction.component); + expectType>(interaction.message); + expectType(interaction.guild); + expectAssignable>(interaction.reply({ fetchReply: true })); + } else if (interaction.inRawGuild()) { + expectAssignable(interaction); + expectType(interaction.component); + expectType>(interaction.message); + expectType(interaction.guild); + expectType>>(interaction.reply({ fetchReply: true })); + } else if (interaction.inGuild()) { + expectAssignable(interaction); + expectType(interaction.component); + expectType(interaction.message); + expectType(interaction.guild); + expectType>(interaction.reply({ fetchReply: true })); + } + } + if (interaction.inCachedGuild()) { expectAssignable(interaction.member); expectNotType>(interaction); @@ -1170,8 +1208,12 @@ client.on('interactionCreate', async interaction => { expectType(interaction.guildId); } - if (interaction.isContextMenuCommand()) { - expectType(interaction); + if ( + interaction.type === InteractionType.ApplicationCommand && + (interaction.commandType === ApplicationCommandType.User || + interaction.commandType === ApplicationCommandType.Message) + ) { + expectType(interaction); if (interaction.inCachedGuild()) { expectAssignable(interaction); expectAssignable(interaction.guild); @@ -1185,7 +1227,10 @@ client.on('interactionCreate', async interaction => { } } - if (interaction.isMessageContextMenuCommand()) { + if ( + interaction.type === InteractionType.ApplicationCommand && + interaction.commandType === ApplicationCommandType.Message + ) { expectType(interaction.targetMessage); if (interaction.inCachedGuild()) { expectType>(interaction.targetMessage); @@ -1196,7 +1241,7 @@ client.on('interactionCreate', async interaction => { } } - if (interaction.isButton()) { + if (interaction.type === InteractionType.MessageComponent && interaction.componentType === ComponentType.Button) { expectType(interaction); expectType(interaction.component); expectType(interaction.message); @@ -1221,32 +1266,7 @@ client.on('interactionCreate', async interaction => { } } - if (interaction.isMessageComponent()) { - expectType(interaction); - expectType(interaction.component); - expectType(interaction.message); - if (interaction.inCachedGuild()) { - expectAssignable(interaction); - expectType(interaction.component); - expectType>(interaction.message); - expectType(interaction.guild); - expectAssignable>(interaction.reply({ fetchReply: true })); - } else if (interaction.inRawGuild()) { - expectAssignable(interaction); - expectType(interaction.component); - expectType>(interaction.message); - expectType(interaction.guild); - expectType>>(interaction.reply({ fetchReply: true })); - } else if (interaction.inGuild()) { - expectAssignable(interaction); - expectType(interaction.component); - expectType(interaction.message); - expectType(interaction.guild); - expectType>(interaction.reply({ fetchReply: true })); - } - } - - if (interaction.isSelectMenu()) { + if (interaction.type === InteractionType.MessageComponent && interaction.componentType === ComponentType.SelectMenu) { expectType(interaction); expectType(interaction.component); expectType(interaction.message); @@ -1271,7 +1291,10 @@ client.on('interactionCreate', async interaction => { } } - if (interaction.isChatInputCommand()) { + if ( + interaction.type === InteractionType.ApplicationCommand && + interaction.commandType === ApplicationCommandType.ChatInput + ) { if (interaction.inRawGuild()) { expectNotAssignable>(interaction); expectAssignable(interaction); @@ -1334,7 +1357,11 @@ client.on('interactionCreate', async interaction => { interaction.reply('test'); } - if (interaction.isChatInputCommand() && interaction.isRepliable()) { + if ( + interaction.type === InteractionType.ApplicationCommand && + interaction.commandType === ApplicationCommandType.ChatInput && + interaction.isRepliable() + ) { expectAssignable(interaction); expectAssignable(interaction); } @@ -1432,16 +1459,14 @@ declare const GuildBasedChannel: GuildBasedChannel; declare const NonThreadGuildBasedChannel: NonThreadGuildBasedChannel; declare const GuildTextBasedChannel: GuildTextBasedChannel; -expectType(TextBasedChannel); +expectType(TextBasedChannel); expectType( TextBasedChannelTypes, ); expectType(VoiceBasedChannel); -expectType( - GuildBasedChannel, -); +expectType(GuildBasedChannel); expectType(NonThreadGuildBasedChannel); -expectType(GuildTextBasedChannel); +expectType(GuildTextBasedChannel); const button = new ButtonBuilder({ label: 'test',