import { type AutocompleteInteraction, type User, type Attachment, type Role, type GuildBasedChannel, type ApplicationCommandOptionChoiceData, } from "discord.js"; export const OPTION_TYPES = { subcommand: 1, subcommandGroup: 2, string: 3, integer: 4, boolean: 5, user: 6, channel: 7, role: 8, mentionable: 9, number: 10, attachment: 11, } as const; type OptionType = keyof typeof OPTION_TYPES; export interface Option { type: OptionType; description?: string; required: Req; autocomplete?: ( interaction: AutocompleteInteraction, value: T, ) => Promise | ApplicationCommandOptionChoiceData[]; __type?: T; } export interface CommandOption extends Option { description: string; } export type InferOptions = { [K in keyof O]: O[K] extends Option ? Required extends true ? Type : Type | undefined : never; }; export type AutocompleteFn = ( interaction: AutocompleteInteraction, value: T, ) => Promise | ApplicationCommandOptionChoiceData[]; export interface OptionBuilder { ( description: string, config: { required: true; autocomplete?: AutocompleteFn }, ): CommandOption; ( description: string, config?: { required?: false; autocomplete?: AutocompleteFn }, ): CommandOption; (config: { required: true; autocomplete?: AutocompleteFn }): Option; (config?: { required?: false; autocomplete?: AutocompleteFn }): Option; required( description: string, config?: { autocomplete?: AutocompleteFn }, ): CommandOption; optional( description: string, config?: { autocomplete?: AutocompleteFn }, ): CommandOption; } const createOption = (type: OptionType): OptionBuilder => { const fn = ( descriptionOrConfig?: string | { required?: boolean; autocomplete?: AutocompleteFn }, config?: { required?: boolean; autocomplete?: AutocompleteFn }, ): any => { const hasDescription = typeof descriptionOrConfig === "string"; const description = hasDescription ? descriptionOrConfig : undefined; const actualConfig = hasDescription ? config : descriptionOrConfig; return { type, description, required: actualConfig?.required ?? false, autocomplete: actualConfig?.autocomplete, }; }; fn.required = (description: string, config?: { autocomplete?: AutocompleteFn }) => { return fn(description, { required: true, autocomplete: config?.autocomplete }); }; fn.optional = (description: string, config?: { autocomplete?: AutocompleteFn }) => { return fn(description, { required: false, autocomplete: config?.autocomplete }); }; return fn as OptionBuilder; }; export const option = { string: createOption("string"), integer: createOption("integer"), number: createOption("number"), boolean: createOption("boolean"), user: createOption("user"), channel: createOption("channel"), role: createOption("role"), mentionable: createOption("mentionable"), attachment: createOption("attachment"), };