mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-13 01:53:30 +01:00
feat(CommandInteractionOptionResolver): add channelTypes option to getChannel (#8934)
* feat(CommandInteractionOptionResolver): add `channelTypes` option to `getChannel` * fix: thread types Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
@@ -135,6 +135,7 @@
|
|||||||
* @property {'CommandInteractionOptionEmpty'} CommandInteractionOptionEmpty
|
* @property {'CommandInteractionOptionEmpty'} CommandInteractionOptionEmpty
|
||||||
* @property {'CommandInteractionOptionNoSubcommand'} CommandInteractionOptionNoSubcommand
|
* @property {'CommandInteractionOptionNoSubcommand'} CommandInteractionOptionNoSubcommand
|
||||||
* @property {'CommandInteractionOptionNoSubcommandGroup'} CommandInteractionOptionNoSubcommandGroup
|
* @property {'CommandInteractionOptionNoSubcommandGroup'} CommandInteractionOptionNoSubcommandGroup
|
||||||
|
* @property {'CommandInteractionOptionInvalidChannelType'} CommandInteractionOptionInvalidChannelType
|
||||||
* @property {'AutocompleteInteractionOptionNoFocusedOption'} AutocompleteInteractionOptionNoFocusedOption
|
* @property {'AutocompleteInteractionOptionNoFocusedOption'} AutocompleteInteractionOptionNoFocusedOption
|
||||||
|
|
||||||
* @property {'ModalSubmitInteractionFieldNotFound'} ModalSubmitInteractionFieldNotFound
|
* @property {'ModalSubmitInteractionFieldNotFound'} ModalSubmitInteractionFieldNotFound
|
||||||
@@ -281,6 +282,7 @@ const keys = [
|
|||||||
'CommandInteractionOptionEmpty',
|
'CommandInteractionOptionEmpty',
|
||||||
'CommandInteractionOptionNoSubcommand',
|
'CommandInteractionOptionNoSubcommand',
|
||||||
'CommandInteractionOptionNoSubcommandGroup',
|
'CommandInteractionOptionNoSubcommandGroup',
|
||||||
|
'CommandInteractionOptionInvalidChannelType',
|
||||||
'AutocompleteInteractionOptionNoFocusedOption',
|
'AutocompleteInteractionOptionNoFocusedOption',
|
||||||
|
|
||||||
'ModalSubmitInteractionFieldNotFound',
|
'ModalSubmitInteractionFieldNotFound',
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ const Messages = {
|
|||||||
`Required option "${name}" is of type: ${type}; expected a non-empty value.`,
|
`Required option "${name}" is of type: ${type}; expected a non-empty value.`,
|
||||||
[DjsErrorCodes.CommandInteractionOptionNoSubcommand]: 'No subcommand specified for interaction.',
|
[DjsErrorCodes.CommandInteractionOptionNoSubcommand]: 'No subcommand specified for interaction.',
|
||||||
[DjsErrorCodes.CommandInteractionOptionNoSubcommandGroup]: 'No subcommand group specified for interaction.',
|
[DjsErrorCodes.CommandInteractionOptionNoSubcommandGroup]: 'No subcommand group specified for interaction.',
|
||||||
|
[DjsErrorCodes.CommandInteractionOptionInvalidChannelType]: (name, type, expected) =>
|
||||||
|
`The type of channel of the option "${name}" is: ${type}; expected ${expected}.`,
|
||||||
[DjsErrorCodes.AutocompleteInteractionOptionNoFocusedOption]: 'No focused option for autocomplete interaction.',
|
[DjsErrorCodes.AutocompleteInteractionOptionNoFocusedOption]: 'No focused option for autocomplete interaction.',
|
||||||
|
|
||||||
[DjsErrorCodes.ModalSubmitInteractionFieldNotFound]: customId =>
|
[DjsErrorCodes.ModalSubmitInteractionFieldNotFound]: customId =>
|
||||||
|
|||||||
@@ -142,12 +142,24 @@ class CommandInteractionOptionResolver {
|
|||||||
* Gets a channel option.
|
* Gets a channel option.
|
||||||
* @param {string} name The name of the option.
|
* @param {string} name The name of the option.
|
||||||
* @param {boolean} [required=false] Whether to throw an error if the option is not found.
|
* @param {boolean} [required=false] Whether to throw an error if the option is not found.
|
||||||
|
* @param {ChannelType[]} [channelTypes=[]] The allowed types of channels. If empty, all channel types are allowed.
|
||||||
* @returns {?(GuildChannel|ThreadChannel|APIChannel)}
|
* @returns {?(GuildChannel|ThreadChannel|APIChannel)}
|
||||||
* The value of the option, or null if not set and not required.
|
* The value of the option, or null if not set and not required.
|
||||||
*/
|
*/
|
||||||
getChannel(name, required = false) {
|
getChannel(name, required = false, channelTypes = []) {
|
||||||
const option = this._getTypedOption(name, [ApplicationCommandOptionType.Channel], ['channel'], required);
|
const option = this._getTypedOption(name, [ApplicationCommandOptionType.Channel], ['channel'], required);
|
||||||
return option?.channel ?? null;
|
const channel = option?.channel ?? null;
|
||||||
|
|
||||||
|
if (channel && channelTypes.length > 0 && !channelTypes.includes(channel.type)) {
|
||||||
|
throw new DiscordjsTypeError(
|
||||||
|
ErrorCodes.CommandInteractionOptionInvalidChannelType,
|
||||||
|
name,
|
||||||
|
channel.type,
|
||||||
|
channelTypes.join(', '),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
32
packages/discord.js/typings/index.d.ts
vendored
32
packages/discord.js/typings/index.d.ts
vendored
@@ -1113,8 +1113,36 @@ export class CommandInteractionOptionResolver<Cached extends CacheType = CacheTy
|
|||||||
public getSubcommandGroup(required?: boolean): string | null;
|
public getSubcommandGroup(required?: boolean): string | null;
|
||||||
public getBoolean(name: string, required: true): boolean;
|
public getBoolean(name: string, required: true): boolean;
|
||||||
public getBoolean(name: string, required?: boolean): boolean | null;
|
public getBoolean(name: string, required?: boolean): boolean | null;
|
||||||
public getChannel(name: string, required: true): NonNullable<CommandInteractionOption<Cached>['channel']>;
|
public getChannel<T extends ChannelType = ChannelType>(
|
||||||
public getChannel(name: string, required?: boolean): NonNullable<CommandInteractionOption<Cached>['channel']> | null;
|
name: string,
|
||||||
|
required: true,
|
||||||
|
channelTypes?: T[],
|
||||||
|
): Extract<
|
||||||
|
NonNullable<CommandInteractionOption<Cached>['channel']>,
|
||||||
|
{
|
||||||
|
// The `type` property of the PublicThreadChannel class is typed as `ChannelType.PublicThread | ChannelType.AnnouncementThread`
|
||||||
|
// If the user only passed one of those channel types, the Extract<> would have resolved to `never`
|
||||||
|
// Hence the need for this ternary
|
||||||
|
type: T extends ChannelType.PublicThread | ChannelType.AnnouncementThread
|
||||||
|
? ChannelType.PublicThread | ChannelType.AnnouncementThread
|
||||||
|
: T;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
public getChannel<T extends ChannelType = ChannelType>(
|
||||||
|
name: string,
|
||||||
|
required?: boolean,
|
||||||
|
channelTypes?: T[],
|
||||||
|
): Extract<
|
||||||
|
NonNullable<CommandInteractionOption<Cached>['channel']>,
|
||||||
|
{
|
||||||
|
// The `type` property of the PublicThreadChannel class is typed as `ChannelType.PublicThread | ChannelType.AnnouncementThread`
|
||||||
|
// If the user only passed one of those channel types, the Extract<> would have resolved to `never`
|
||||||
|
// Hence the need for this ternary
|
||||||
|
type: T extends ChannelType.PublicThread | ChannelType.AnnouncementThread
|
||||||
|
? ChannelType.PublicThread | ChannelType.AnnouncementThread
|
||||||
|
: T;
|
||||||
|
}
|
||||||
|
> | null;
|
||||||
public getString(name: string, required: true): string;
|
public getString(name: string, required: true): string;
|
||||||
public getString(name: string, required?: boolean): string | null;
|
public getString(name: string, required?: boolean): string | null;
|
||||||
public getInteger(name: string, required: true): number;
|
public getInteger(name: string, required: true): number;
|
||||||
|
|||||||
@@ -152,6 +152,8 @@ import {
|
|||||||
AutoModerationActionExecution,
|
AutoModerationActionExecution,
|
||||||
AutoModerationRule,
|
AutoModerationRule,
|
||||||
AutoModerationRuleManager,
|
AutoModerationRuleManager,
|
||||||
|
PrivateThreadChannel,
|
||||||
|
PublicThreadChannel,
|
||||||
} from '.';
|
} from '.';
|
||||||
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
||||||
import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders';
|
import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders';
|
||||||
@@ -1744,6 +1746,22 @@ client.on('interactionCreate', async interaction => {
|
|||||||
|
|
||||||
expectType<GuildBasedChannel>(interaction.options.getChannel('test', true));
|
expectType<GuildBasedChannel>(interaction.options.getChannel('test', true));
|
||||||
expectType<Role>(interaction.options.getRole('test', true));
|
expectType<Role>(interaction.options.getRole('test', true));
|
||||||
|
|
||||||
|
expectType<PublicThreadChannel>(interaction.options.getChannel('test', true, [ChannelType.PublicThread]));
|
||||||
|
expectType<PublicThreadChannel>(interaction.options.getChannel('test', true, [ChannelType.AnnouncementThread]));
|
||||||
|
expectType<PublicThreadChannel>(
|
||||||
|
interaction.options.getChannel('test', true, [ChannelType.PublicThread, ChannelType.AnnouncementThread]),
|
||||||
|
);
|
||||||
|
expectType<PrivateThreadChannel>(interaction.options.getChannel('test', true, [ChannelType.PrivateThread]));
|
||||||
|
|
||||||
|
expectType<TextChannel>(interaction.options.getChannel('test', true, [ChannelType.GuildText]));
|
||||||
|
expectType<TextChannel | null>(interaction.options.getChannel('test', false, [ChannelType.GuildText]));
|
||||||
|
expectType<ForumChannel | VoiceChannel>(
|
||||||
|
interaction.options.getChannel('test', true, [ChannelType.GuildForum, ChannelType.GuildVoice]),
|
||||||
|
);
|
||||||
|
expectType<ForumChannel | VoiceChannel | null>(
|
||||||
|
interaction.options.getChannel('test', false, [ChannelType.GuildForum, ChannelType.GuildVoice]),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
consumeCachedCommand(interaction);
|
consumeCachedCommand(interaction);
|
||||||
|
|||||||
Reference in New Issue
Block a user