feat: allow RestOrArray for command option builders (#10175)

* feat(builders): allow RestOrArray for command option builders

change ApplicationCommandOption methods to allow both rest and array params,
which previously wasn't consistent with other builders.

* chore: merge imports

---------

Co-authored-by: almeidx <github@almeidx.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
LeCarbonator 2024-03-24 19:12:05 +01:00 committed by GitHub
parent ddc927fabd
commit a1a3a95c94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 19 deletions

View file

@ -144,23 +144,22 @@ describe('Slash Commands', () => {
integer
.setName('iscool')
.setDescription('Are we cool or what?')
.addChoices({ name: 'Very cool', value: 1_000 }),
.addChoices({ name: 'Very cool', value: 1_000 })
.addChoices([{ name: 'Even cooler', value: 2_000 }]),
)
.addNumberOption((number) =>
number
.setName('iscool')
.setDescription('Are we cool or what?')
.addChoices({ name: 'Very cool', value: 1.5 }),
.addChoices({ name: 'Very cool', value: 1.5 })
.addChoices([{ name: 'Even cooler', value: 2.5 }]),
)
.addStringOption((string) =>
string
.setName('iscool')
.setDescription('Are we cool or what?')
.addChoices(
{ name: 'Fancy Pants', value: 'fp_1' },
{ name: 'Fancy Shoes', value: 'fs_1' },
{ name: 'The Whole shebang', value: 'all' },
),
.addChoices({ name: 'Fancy Pants', value: 'fp_1' }, { name: 'Fancy Shoes', value: 'fs_1' })
.addChoices([{ name: 'The Whole shebang', value: 'all' }]),
)
.addIntegerOption((integer) =>
integer.setName('iscool').setDescription('Are we cool or what?').setAutocomplete(true),
@ -229,7 +228,9 @@ describe('Slash Commands', () => {
test('GIVEN a builder with valid channel options and channel_types THEN does not throw an error', () => {
expect(() =>
getBuilder().addChannelOption(getChannelOption().addChannelTypes(ChannelType.GuildText)),
getBuilder().addChannelOption(
getChannelOption().addChannelTypes(ChannelType.GuildText).addChannelTypes([ChannelType.GuildVoice]),
),
).not.toThrowError();
expect(() => {

View file

@ -1,5 +1,6 @@
import { s } from '@sapphire/shapeshift';
import { ChannelType } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray';
/**
* The allowed channel types used for a channel option in a slash command builder.
@ -41,12 +42,12 @@ export class ApplicationCommandOptionChannelTypesMixin {
*
* @param channelTypes - The channel types
*/
public addChannelTypes(...channelTypes: ApplicationCommandOptionAllowedChannelTypes[]) {
public addChannelTypes(...channelTypes: RestOrArray<ApplicationCommandOptionAllowedChannelTypes>) {
if (this.channel_types === undefined) {
Reflect.set(this, 'channel_types', []);
}
this.channel_types!.push(...channelTypesPredicate.parse(channelTypes));
this.channel_types!.push(...channelTypesPredicate.parse(normalizeArray(channelTypes)));
return this;
}

View file

@ -1,5 +1,6 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandOptionType, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray.js';
import { localizationMapPredicate, validateChoicesLength } from '../Assertions.js';
const stringPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100);
@ -31,20 +32,21 @@ export class ApplicationCommandOptionWithChoicesMixin<ChoiceType extends number
*
* @param choices - The choices to add
*/
public addChoices(...choices: APIApplicationCommandOptionChoice<ChoiceType>[]): this {
if (choices.length > 0 && 'autocomplete' in this && this.autocomplete) {
public addChoices(...choices: RestOrArray<APIApplicationCommandOptionChoice<ChoiceType>>): this {
const normalizedChoices = normalizeArray(choices);
if (normalizedChoices.length > 0 && 'autocomplete' in this && this.autocomplete) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
choicesPredicate.parse(choices);
choicesPredicate.parse(normalizedChoices);
if (this.choices === undefined) {
Reflect.set(this, 'choices', []);
}
validateChoicesLength(choices.length, this.choices);
validateChoicesLength(normalizedChoices.length, this.choices);
for (const { name, name_localizations, value } of choices) {
for (const { name, name_localizations, value } of normalizedChoices) {
// Validate the value
if (this.type === ApplicationCommandOptionType.String) {
stringPredicate.parse(value);
@ -63,15 +65,16 @@ export class ApplicationCommandOptionWithChoicesMixin<ChoiceType extends number
*
* @param choices - The choices to set
*/
public setChoices<Input extends APIApplicationCommandOptionChoice<ChoiceType>[]>(...choices: Input): this {
if (choices.length > 0 && 'autocomplete' in this && this.autocomplete) {
public setChoices<Input extends APIApplicationCommandOptionChoice<ChoiceType>>(...choices: RestOrArray<Input>): this {
const normalizedChoices = normalizeArray(choices);
if (normalizedChoices.length > 0 && 'autocomplete' in this && this.autocomplete) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
choicesPredicate.parse(choices);
choicesPredicate.parse(normalizedChoices);
Reflect.set(this, 'choices', []);
this.addChoices(...choices);
this.addChoices(normalizedChoices);
return this;
}