From ba915f93ce0907828ba17b2d5ae009631ceb860d Mon Sep 17 00:00:00 2001 From: Jeroen Claassens Date: Sat, 2 Nov 2024 16:24:39 +0100 Subject: [PATCH] refactor: resolve several sonar issues (#823) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: resolve several sonar issues Mostly code smells and 2 deprecations * refactor: use `If` to improve overloads * fix: build errors and Vlad's request * chore: commit committed * chore: bugger * chore: fixed what was broken. * refactor: target is object * refactor: `const Required` -> `Required` * refactor: fewer array index accessors --------- Co-authored-by: Aura Román --- packages/async-queue/src/lib/AsyncQueue.ts | 2 +- packages/decorators/src/base-decorators.ts | 7 +- .../src/lib/InteractionOptionResolver.ts | 94 ++++++++----------- .../lib/MessagePrompter/MessagePrompter.ts | 2 +- .../lib/PaginatedMessages/PaginatedMessage.ts | 19 ++-- .../PaginatedMessageTypes.ts | 2 +- packages/event-iterator/src/index.ts | 8 +- .../event-iterator/tests/lib/MockEmitter.ts | 2 +- scripts/vitest.config.ts | 4 +- 9 files changed, 63 insertions(+), 77 deletions(-) diff --git a/packages/async-queue/src/lib/AsyncQueue.ts b/packages/async-queue/src/lib/AsyncQueue.ts index db23bfc812..5ef68bc50f 100644 --- a/packages/async-queue/src/lib/AsyncQueue.ts +++ b/packages/async-queue/src/lib/AsyncQueue.ts @@ -23,7 +23,7 @@ export class AsyncQueue { /** * The promises array */ - private promises: AsyncQueueEntry[] = []; + private readonly promises: AsyncQueueEntry[] = []; /** * Waits for last promise and queues a new one diff --git a/packages/decorators/src/base-decorators.ts b/packages/decorators/src/base-decorators.ts index b5af3b2118..b93b283ccd 100644 --- a/packages/decorators/src/base-decorators.ts +++ b/packages/decorators/src/base-decorators.ts @@ -1,4 +1,3 @@ -import type { NonNullObject } from '@sapphire/utilities'; import { createMethodDecorator } from './utils'; /** @@ -6,11 +5,11 @@ import { createMethodDecorator } from './utils'; * @param value Whether the property should be enumerable or not */ export function Enumerable(value: boolean) { - return (target: unknown, key: string) => { - Reflect.defineProperty(target as NonNullObject, key, { + return (target: object, key: string) => { + Reflect.defineProperty(target, key, { enumerable: value, set(this: unknown, val: unknown) { - Reflect.defineProperty(this as NonNullObject, key, { + Reflect.defineProperty(this as object, key, { configurable: true, enumerable: value, value: val, diff --git a/packages/discord-utilities/src/lib/InteractionOptionResolver.ts b/packages/discord-utilities/src/lib/InteractionOptionResolver.ts index ee46b11955..c8af9cfb27 100644 --- a/packages/discord-utilities/src/lib/InteractionOptionResolver.ts +++ b/packages/discord-utilities/src/lib/InteractionOptionResolver.ts @@ -10,7 +10,7 @@ import { type APIApplicationCommandInteractionDataOption, type APIApplicationCommandInteractionDataStringOption, type APIAttachment, - type APIChatInputApplicationCommandInteractionDataResolved, + type APIInteractionDataResolved, type APIInteractionDataResolvedChannel, type APIInteractionDataResolvedGuildMember, type APIMessage, @@ -18,7 +18,7 @@ import { type APIModalSubmitInteraction, type APIRole, type APIUser, - type APIUserApplicationCommandInteractionDataResolved + type APIUserInteractionDataResolved } from 'discord-api-types/v10'; /** @@ -37,8 +37,8 @@ export class InteractionOptionResolver { * The interaction resolved data */ private readonly resolved: - | APIChatInputApplicationCommandInteractionDataResolved - | APIUserApplicationCommandInteractionDataResolved + | APIInteractionDataResolved + | APIUserInteractionDataResolved | APIMessageApplicationCommandInteractionDataResolved | null = null; @@ -80,14 +80,13 @@ export class InteractionOptionResolver { } } - public get(name: string, required?: boolean | false): APIApplicationCommandInteractionDataOption | null; - public get(name: string, required: true): APIApplicationCommandInteractionDataOption; - /** * Gets an option by its name * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public get(name: string, required?: Required): RequiredIf; + public get(name: string, required = false): APIApplicationCommandInteractionDataOption | null { const option = this.hoistedOptions?.find((opt) => opt.name === name); if (!option) { @@ -101,13 +100,11 @@ export class InteractionOptionResolver { return option; } - public getSubcommand(required?: boolean | false): string | null; - public getSubcommand(required: true): string; - /** * Gets the selected subcommand * @param required Whether to throw an error if there is no subcommand */ + public getSubcommand(required?: Required): RequiredIf; public getSubcommand(required = true): string | null { if (required && !this.subcommand) { throw new Error('A subcommand was not selected'); @@ -116,13 +113,11 @@ export class InteractionOptionResolver { return this.subcommand; } - public getSubcommandGroup(required?: boolean | false): string | null; - public getSubcommandGroup(required: true): string; - /** * Gets the selected subcommand group * @param required Whether to throw an error if there is no subcommand group */ + public getSubcommandGroup(required?: Required): RequiredIf; public getSubcommandGroup(required = true): string | null { if (required && !this.group) { throw new Error('A subcommand group was not selected'); @@ -131,131 +126,120 @@ export class InteractionOptionResolver { return this.group; } - public getBoolean(name: string, required?: boolean | false): boolean | null; - public getBoolean(name: string, required: true): boolean; - /** * Gets a boolean option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getBoolean(name: string, required?: Required): RequiredIf; public getBoolean(name: string, required = false): boolean | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.Boolean, required); return option?.value ?? null; } - public getChannel(name: string, required?: boolean | false): APIInteractionDataResolvedChannel | null; - public getChannel(name: string, required: true): APIInteractionDataResolvedChannel; - /** * Gets a channel option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getChannel(name: string, required?: Required): RequiredIf; + public getChannel(name: string, required = false): APIInteractionDataResolvedChannel | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.Channel, required); return option && this.resolved && 'channels' in this.resolved ? (this.resolved.channels?.[option.value] ?? null) : null; } - public getString(name: string, required?: boolean | false): string | null; - public getString(name: string, required: true): string; - /** * Gets a string option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getString(name: string, required?: Required): RequiredIf; public getString(name: string, required = false): string | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.String, required); return option?.value ?? null; } - public getInteger(name: string, required?: boolean | false): number | null; - public getInteger(name: string, required: true): number; - /** * Gets an integer option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getInteger(name: string, required?: Required): RequiredIf; public getInteger(name: string, required = false): number | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.Integer, required); return option?.value ?? null; } - public getNumber(name: string, required?: boolean | false): number | null; - public getNumber(name: string, required: true): number; - /** * Gets a number option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getNumber(name: string, required?: Required): RequiredIf; public getNumber(name: string, required = false): number | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.Number, required); return option?.value ?? null; } - public getUser(name: string, required?: boolean | false): APIUser | null; - public getUser(name: string, required: true): APIUser; - /** * Gets a user option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getUser(name: string, required?: Required): RequiredIf; public getUser(name: string, required = false): APIUser | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.User, required); return option && this.resolved && 'users' in this.resolved ? (this.resolved.users?.[option.value] ?? null) : null; } - public getMember(name: string, required?: boolean | false): APIInteractionDataResolvedGuildMember | null; - public getMember(name: string, required: true): APIInteractionDataResolvedGuildMember; - /** * Gets a member option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getMember( + name: string, + required?: Required + ): RequiredIf; + public getMember(name: string, required = false): APIInteractionDataResolvedGuildMember | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.User, required); return option && this.resolved && 'members' in this.resolved ? (this.resolved.members?.[option.value] ?? null) : null; } - public getRole(name: string, required?: boolean | false): APIRole | null; - public getRole(name: string, required: true): APIRole; - /** * Gets a role option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getRole(name: string, required?: Required): RequiredIf; public getRole(name: string, required = false): APIRole | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.Role, required); return option && this.resolved && 'roles' in this.resolved ? (this.resolved.roles?.[option.value] ?? null) : null; } - public getAttachment(name: string, required?: boolean | false): APIAttachment | null; - public getAttachment(name: string, required: true): APIAttachment; - /** * Gets an attachment option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getAttachment(name: string, required?: Required): RequiredIf; public getAttachment(name: string, required = false): APIAttachment | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.Attachment, required); return option && this.resolved && 'attachments' in this.resolved ? (this.resolved.attachments?.[option.value] ?? null) : null; } - public getMentionable(name: string, required?: boolean | false): APIUser | APIInteractionDataResolvedGuildMember | APIRole | null; - public getMentionable(name: string, required: true): APIUser | APIInteractionDataResolvedGuildMember | APIRole; - /** * Gets a mentionable option * @param name The name of the option * @param required Whether to throw an error if the option is not found */ + public getMentionable( + name: string, + required?: Required + ): RequiredIf; + public getMentionable(name: string, required = false): APIUser | APIInteractionDataResolvedGuildMember | APIRole | null { const option = this.getTypedOption(name, ApplicationCommandOptionType.Mentionable, required); @@ -286,22 +270,20 @@ export class InteractionOptionResolver { throw new Error('This method can only be used on user context menu interactions'); } - return (this.resolved as APIUserApplicationCommandInteractionDataResolved).users[this.interaction.data.target_id]; + return (this.resolved as APIUserInteractionDataResolved).users[this.interaction.data.target_id]; } - public getTargetMember(required?: boolean | false): APIInteractionDataResolvedGuildMember | null; - public getTargetMember(required: true): APIInteractionDataResolvedGuildMember; - /** * Gets the target member for a context menu interaction * @param required Whether to throw an error if the member data is not present */ + public getTargetMember(required?: Required): RequiredIf; public getTargetMember(required = false): APIInteractionDataResolvedGuildMember | null { if (this.interaction.type !== InteractionType.ApplicationCommand || this.interaction.data.type !== ApplicationCommandType.User) { throw new Error('This method can only be used on user context menu interactions'); } - const member = (this.resolved as APIUserApplicationCommandInteractionDataResolved).members?.[this.interaction.data.target_id] ?? null; + const member = (this.resolved as APIUserInteractionDataResolved).members?.[this.interaction.data.target_id] ?? null; if (!member && required) { throw new Error('Member data is not present'); @@ -345,13 +327,11 @@ export class InteractionOptionResolver { return option; } - private getTypedOption