From b059fa14b3b76b64601bf9fcc42a67d6b32032ca Mon Sep 17 00:00:00 2001 From: MARCROCK22 <57925328+MARCROCK22@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:50:57 -0400 Subject: [PATCH] feat: option.value typing with choices (#278) --- src/commands/applications/chat.ts | 62 ++++-------- src/commands/applications/options.ts | 136 +++++++++++++++++++++------ 2 files changed, 126 insertions(+), 72 deletions(-) diff --git a/src/commands/applications/chat.ts b/src/commands/applications/chat.ts index f024592..fb9282f 100644 --- a/src/commands/applications/chat.ts +++ b/src/commands/applications/chat.ts @@ -5,6 +5,9 @@ import type { MenuCommandContext, ModalContext, PermissionStrings, + SeyfertBaseChoiceableOption, + SeyfertBasicOption, + SeyfertChoice, SeyfertNumberOption, SeyfertStringOption, } from '../..'; @@ -15,7 +18,7 @@ import type { OptionResolverStructure, UserStructure, } from '../../client/transformers'; -import { type Awaitable, type FlatObjectKeys, magicImport } from '../../common'; +import { magicImport } from '../../common'; import type { AllChannels, AutocompleteInteraction } from '../../structures'; import { type APIApplicationCommandBasicOption, @@ -30,10 +33,8 @@ import { import type { Groups, RegisteredMiddlewares } from '../decorators'; import type { CommandContext } from './chatcontext'; import type { - DefaultLocale, ExtraProps, IgnoreCommand, - OKFunction, OnOptionsReturnObject, PassFunction, StopFunction, @@ -54,47 +55,22 @@ export interface ReturnOptionsTypes { 11: Attachment; } -type Wrap = N extends - | ApplicationCommandOptionType.Subcommand - | ApplicationCommandOptionType.SubcommandGroup - ? never - : { - required?: boolean; - value?( - data: { context: CommandContext; value: ReturnOptionsTypes[N] }, - ok: OKFunction, - fail: StopFunction, - ): Awaitable; - } & { - description: string; - description_localizations?: APIApplicationCommandBasicOption['description_localizations']; - name_localizations?: APIApplicationCommandBasicOption['name_localizations']; - locales?: { - name?: FlatObjectKeys; - description?: FlatObjectKeys; - }; - }; - -export type __TypeWrapper = Wrap; - -export type __TypesWrapper = { - [P in keyof typeof ApplicationCommandOptionType]: `${(typeof ApplicationCommandOptionType)[P]}` extends `${infer D extends - number}` - ? Wrap - : never; -}; - export type AutocompleteCallback = (interaction: AutocompleteInteraction) => any; export type OnAutocompleteErrorCallback = (interaction: AutocompleteInteraction, error: unknown) => any; -export type CommandBaseOption = __TypesWrapper[keyof __TypesWrapper]; -export type CommandBaseAutocompleteOption = __TypesWrapper[keyof __TypesWrapper] & { +export type CommandBaseOption = + | SeyfertBaseChoiceableOption + | SeyfertBasicOption; +export type CommandBaseAutocompleteOption = ( + | SeyfertBasicOption + | SeyfertBaseChoiceableOption +) & { autocomplete: AutocompleteCallback; onAutocompleteError?: OnAutocompleteErrorCallback; }; export type CommandAutocompleteOption = CommandBaseAutocompleteOption & { name: string }; -export type __CommandOption = CommandBaseOption; //| CommandBaseAutocompleteOption; -export type CommandOption = __CommandOption & { name: string }; -export type OptionsRecord = Record; +export type CommandOptionWithoutName = CommandBaseOption; +export type CommandOption = CommandOptionWithoutName & { name: string }; +export type OptionsRecord = Record; type KeysWithoutRequired = { [K in keyof T]-?: T[K]['required'] extends true ? never : K; @@ -104,16 +80,16 @@ type ContextOptionsAux = { [K in Exclude>]: T[K]['value'] extends (...args: any) => any ? Parameters[1]>[0] : T[K] extends SeyfertStringOption | SeyfertNumberOption - ? T[K]['choices'] extends NonNullable - ? T[K]['choices'][number]['value'] + ? NonNullable extends SeyfertChoice[] + ? NonNullable[number]['value'] : ReturnOptionsTypes[T[K]['type']] : ReturnOptionsTypes[T[K]['type']]; } & { [K in KeysWithoutRequired]?: T[K]['value'] extends (...args: any) => any ? Parameters[1]>[0] : T[K] extends SeyfertStringOption | SeyfertNumberOption - ? T[K]['choices'] extends NonNullable - ? T[K]['choices'][number]['value'] + ? NonNullable extends SeyfertChoice[] + ? NonNullable[number]['value'] : ReturnOptionsTypes[T[K]['type']] : ReturnOptionsTypes[T[K]['type']]; }; @@ -159,7 +135,7 @@ export class BaseCommand { let errored = false; for (const i of this.options ?? []) { try { - const option = this.options!.find(x => x.name === i.name) as __CommandOption; + const option = this.options!.find(x => x.name === i.name) as CommandOptionWithoutName; const value = resolver.getHoisted(i.name)?.value !== undefined ? await new Promise(async (res, rej) => { diff --git a/src/commands/applications/options.ts b/src/commands/applications/options.ts index c4ca9b1..70ec2de 100644 --- a/src/commands/applications/options.ts +++ b/src/commands/applications/options.ts @@ -4,57 +4,139 @@ import type { MenuCommandContext, OnAutocompleteErrorCallback, ReturnOptionsTypes, - __TypesWrapper, } from '..'; +import type { Awaitable, FlatObjectKeys } from '../../common'; import type { ModalContext } from '../../components'; import type { ComponentContext } from '../../components/componentcontext'; import type { MessageCommandInteraction, UserCommandInteraction } from '../../structures'; -import { type APIApplicationCommandOptionChoice, ApplicationCommandOptionType, type ChannelType } from '../../types'; +import { + type APIApplicationCommandBasicOption, + type APIApplicationCommandOptionChoice, + ApplicationCommandOptionType, + type ChannelType, +} from '../../types'; import type { CommandContext } from './chatcontext'; -import type { MiddlewareContext } from './shared'; +import type { DefaultLocale, MiddlewareContext, OKFunction, StopFunction } from './shared'; -export type SeyfertBasicOption = __TypesWrapper[T] & D; +export interface SeyfertBasicOption { + required?: boolean; + value?( + data: { context: CommandContext; value: ReturnOptionsTypes[T] }, + ok: OKFunction, + fail: StopFunction, + ): Awaitable; + description: string; + description_localizations?: APIApplicationCommandBasicOption['description_localizations']; + name_localizations?: APIApplicationCommandBasicOption['name_localizations']; + locales?: { + name?: FlatObjectKeys; + description?: FlatObjectKeys; + }; +} -export type SeyfertStringOption = SeyfertBasicOption<'String'> & { +export interface SeyfertBaseChoiceableOption< + T extends keyof ReturnOptionsTypes, + C = T extends ChoiceableTypes ? SeyfertChoice[] : never, +> { + required?: boolean; + choices?: C; + value?: ValueCallback; + description: string; + description_localizations?: APIApplicationCommandBasicOption['description_localizations']; + name_localizations?: APIApplicationCommandBasicOption['name_localizations']; + locales?: { + name?: FlatObjectKeys; + description?: FlatObjectKeys; + }; +} + +export type SeyfertChoice = + | { readonly name: string; readonly value: T } + | APIApplicationCommandOptionChoice; + +export type ChoiceableTypes = + | ApplicationCommandOptionType.String + | ApplicationCommandOptionType.Integer + | ApplicationCommandOptionType.Number; + +export interface ChoiceableValues { + [ApplicationCommandOptionType.String]: string; + [ApplicationCommandOptionType.Number]: number; + [ApplicationCommandOptionType.Integer]: number; +} + +export type ValueCallback< + T extends keyof ReturnOptionsTypes, + C = T extends ChoiceableTypes ? SeyfertChoice[] : never, +> = ( + data: { + context: CommandContext; + value: T extends ChoiceableTypes + ? C extends SeyfertChoice[] + ? C[number]['value'] extends ReturnOptionsTypes[T] + ? C[number]['value'] + : ReturnOptionsTypes[T] + : ReturnOptionsTypes[T] + : ReturnOptionsTypes[T]; + }, + ok: OKFunction, + fail: StopFunction, +) => Awaitable; + +export type SeyfertStringOption[]> = SeyfertBaseChoiceableOption< + ApplicationCommandOptionType.String, + T +> & { autocomplete?: AutocompleteCallback; onAutocompleteError?: OnAutocompleteErrorCallback; - choices?: - | readonly { readonly name: string; readonly value: string }[] - | APIApplicationCommandOptionChoice[]; min_length?: number; max_length?: number; }; -export type SeyfertIntegerOption = SeyfertBasicOption<'Integer'> & { +export type SeyfertIntegerOption[]> = SeyfertBaseChoiceableOption< + ApplicationCommandOptionType.Integer, + T +> & { autocomplete?: AutocompleteCallback; onAutocompleteError?: OnAutocompleteErrorCallback; - choices?: APIApplicationCommandOptionChoice[]; min_value?: number; max_value?: number; }; -export type SeyfertBooleanOption = SeyfertBasicOption<'Boolean'>; -export type SeyfertUserOption = SeyfertBasicOption<'User'>; -export type SeyfertChannelOption = SeyfertBasicOption<'Channel'> & { +export type SeyfertNumberOption[]> = SeyfertBaseChoiceableOption< + ApplicationCommandOptionType.Number, + T +> & { + autocomplete?: AutocompleteCallback; + onAutocompleteError?: OnAutocompleteErrorCallback; + min_value?: number; + max_value?: number; +}; +export type SeyfertBooleanOption = SeyfertBasicOption; +export type SeyfertUserOption = SeyfertBasicOption; +export type SeyfertChannelOption = SeyfertBasicOption & { channel_types?: ChannelType[]; }; -export type SeyfertRoleOption = SeyfertBasicOption<'Role'>; -export type SeyfertMentionableOption = SeyfertBasicOption<'Mentionable'>; -export type SeyfertNumberOption = SeyfertBasicOption<'Number'> & { - autocomplete?: AutocompleteCallback; - onAutocompleteError?: OnAutocompleteErrorCallback; - choices?: APIApplicationCommandOptionChoice[]; - min_value?: number; - max_value?: number; -}; -export type SeyfertAttachmentOption = SeyfertBasicOption<'Attachment'>; +export type SeyfertRoleOption = SeyfertBasicOption; +export type SeyfertMentionableOption = SeyfertBasicOption; +export type SeyfertAttachmentOption = SeyfertBasicOption; -export function createStringOption(data: T) { +export function createStringOption[] = SeyfertChoice[]>( + data: SeyfertStringOption, +) { return { ...data, type: ApplicationCommandOptionType.String } as const; } -export function createIntegerOption(data: T) { +export function createIntegerOption[] = SeyfertChoice[]>( + data: SeyfertIntegerOption, +) { return { ...data, type: ApplicationCommandOptionType.Integer } as const; } +export function createNumberOption[] = SeyfertChoice[]>( + data: SeyfertNumberOption, +) { + return { ...data, type: ApplicationCommandOptionType.Number } as const; +} + export function createBooleanOption(data: T) { return { ...data, type: ApplicationCommandOptionType.Boolean } as const; } @@ -75,10 +157,6 @@ export function createMentionableOption(data: T) { - return { ...data, type: ApplicationCommandOptionType.Number } as const; -} - export function createAttachmentOption(data: T) { return { ...data, type: ApplicationCommandOptionType.Attachment } as const; }