feat: option.value typing with choices (#278)

This commit is contained in:
MARCROCK22 2024-10-13 14:50:57 -04:00 committed by GitHub
parent 517b940bfe
commit b059fa14b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 126 additions and 72 deletions

View File

@ -5,6 +5,9 @@ import type {
MenuCommandContext, MenuCommandContext,
ModalContext, ModalContext,
PermissionStrings, PermissionStrings,
SeyfertBaseChoiceableOption,
SeyfertBasicOption,
SeyfertChoice,
SeyfertNumberOption, SeyfertNumberOption,
SeyfertStringOption, SeyfertStringOption,
} from '../..'; } from '../..';
@ -15,7 +18,7 @@ import type {
OptionResolverStructure, OptionResolverStructure,
UserStructure, UserStructure,
} from '../../client/transformers'; } from '../../client/transformers';
import { type Awaitable, type FlatObjectKeys, magicImport } from '../../common'; import { magicImport } from '../../common';
import type { AllChannels, AutocompleteInteraction } from '../../structures'; import type { AllChannels, AutocompleteInteraction } from '../../structures';
import { import {
type APIApplicationCommandBasicOption, type APIApplicationCommandBasicOption,
@ -30,10 +33,8 @@ import {
import type { Groups, RegisteredMiddlewares } from '../decorators'; import type { Groups, RegisteredMiddlewares } from '../decorators';
import type { CommandContext } from './chatcontext'; import type { CommandContext } from './chatcontext';
import type { import type {
DefaultLocale,
ExtraProps, ExtraProps,
IgnoreCommand, IgnoreCommand,
OKFunction,
OnOptionsReturnObject, OnOptionsReturnObject,
PassFunction, PassFunction,
StopFunction, StopFunction,
@ -54,47 +55,22 @@ export interface ReturnOptionsTypes {
11: Attachment; 11: Attachment;
} }
type Wrap<N extends ApplicationCommandOptionType> = N extends
| ApplicationCommandOptionType.Subcommand
| ApplicationCommandOptionType.SubcommandGroup
? never
: {
required?: boolean;
value?(
data: { context: CommandContext; value: ReturnOptionsTypes[N] },
ok: OKFunction<any>,
fail: StopFunction,
): Awaitable<void>;
} & {
description: string;
description_localizations?: APIApplicationCommandBasicOption['description_localizations'];
name_localizations?: APIApplicationCommandBasicOption['name_localizations'];
locales?: {
name?: FlatObjectKeys<DefaultLocale>;
description?: FlatObjectKeys<DefaultLocale>;
};
};
export type __TypeWrapper<T extends ApplicationCommandOptionType> = Wrap<T>;
export type __TypesWrapper = {
[P in keyof typeof ApplicationCommandOptionType]: `${(typeof ApplicationCommandOptionType)[P]}` extends `${infer D extends
number}`
? Wrap<D>
: never;
};
export type AutocompleteCallback = (interaction: AutocompleteInteraction) => any; export type AutocompleteCallback = (interaction: AutocompleteInteraction) => any;
export type OnAutocompleteErrorCallback = (interaction: AutocompleteInteraction, error: unknown) => any; export type OnAutocompleteErrorCallback = (interaction: AutocompleteInteraction, error: unknown) => any;
export type CommandBaseOption = __TypesWrapper[keyof __TypesWrapper]; export type CommandBaseOption =
export type CommandBaseAutocompleteOption = __TypesWrapper[keyof __TypesWrapper] & { | SeyfertBaseChoiceableOption<ApplicationCommandOptionType>
| SeyfertBasicOption<ApplicationCommandOptionType>;
export type CommandBaseAutocompleteOption = (
| SeyfertBasicOption<ApplicationCommandOptionType>
| SeyfertBaseChoiceableOption<ApplicationCommandOptionType>
) & {
autocomplete: AutocompleteCallback; autocomplete: AutocompleteCallback;
onAutocompleteError?: OnAutocompleteErrorCallback; onAutocompleteError?: OnAutocompleteErrorCallback;
}; };
export type CommandAutocompleteOption = CommandBaseAutocompleteOption & { name: string }; export type CommandAutocompleteOption = CommandBaseAutocompleteOption & { name: string };
export type __CommandOption = CommandBaseOption; //| CommandBaseAutocompleteOption; export type CommandOptionWithoutName = CommandBaseOption;
export type CommandOption = __CommandOption & { name: string }; export type CommandOption = CommandOptionWithoutName & { name: string };
export type OptionsRecord = Record<string, __CommandOption & { type: ApplicationCommandOptionType }>; export type OptionsRecord = Record<string, CommandOptionWithoutName & { type: ApplicationCommandOptionType }>;
type KeysWithoutRequired<T extends OptionsRecord> = { type KeysWithoutRequired<T extends OptionsRecord> = {
[K in keyof T]-?: T[K]['required'] extends true ? never : K; [K in keyof T]-?: T[K]['required'] extends true ? never : K;
@ -104,16 +80,16 @@ type ContextOptionsAux<T extends OptionsRecord> = {
[K in Exclude<keyof T, KeysWithoutRequired<T>>]: T[K]['value'] extends (...args: any) => any [K in Exclude<keyof T, KeysWithoutRequired<T>>]: T[K]['value'] extends (...args: any) => any
? Parameters<Parameters<T[K]['value']>[1]>[0] ? Parameters<Parameters<T[K]['value']>[1]>[0]
: T[K] extends SeyfertStringOption | SeyfertNumberOption : T[K] extends SeyfertStringOption | SeyfertNumberOption
? T[K]['choices'] extends NonNullable<SeyfertStringOption['choices'] | SeyfertNumberOption['choices']> ? NonNullable<T[K]['choices']> extends SeyfertChoice<string | number>[]
? T[K]['choices'][number]['value'] ? NonNullable<T[K]['choices']>[number]['value']
: ReturnOptionsTypes[T[K]['type']] : ReturnOptionsTypes[T[K]['type']]
: ReturnOptionsTypes[T[K]['type']]; : ReturnOptionsTypes[T[K]['type']];
} & { } & {
[K in KeysWithoutRequired<T>]?: T[K]['value'] extends (...args: any) => any [K in KeysWithoutRequired<T>]?: T[K]['value'] extends (...args: any) => any
? Parameters<Parameters<T[K]['value']>[1]>[0] ? Parameters<Parameters<T[K]['value']>[1]>[0]
: T[K] extends SeyfertStringOption | SeyfertNumberOption : T[K] extends SeyfertStringOption | SeyfertNumberOption
? T[K]['choices'] extends NonNullable<SeyfertStringOption['choices'] | SeyfertNumberOption['choices']> ? NonNullable<T[K]['choices']> extends SeyfertChoice<string | number>[]
? T[K]['choices'][number]['value'] ? NonNullable<T[K]['choices']>[number]['value']
: ReturnOptionsTypes[T[K]['type']] : ReturnOptionsTypes[T[K]['type']]
: ReturnOptionsTypes[T[K]['type']]; : ReturnOptionsTypes[T[K]['type']];
}; };
@ -159,7 +135,7 @@ export class BaseCommand {
let errored = false; let errored = false;
for (const i of this.options ?? []) { for (const i of this.options ?? []) {
try { 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 = const value =
resolver.getHoisted(i.name)?.value !== undefined resolver.getHoisted(i.name)?.value !== undefined
? await new Promise(async (res, rej) => { ? await new Promise(async (res, rej) => {

View File

@ -4,57 +4,139 @@ import type {
MenuCommandContext, MenuCommandContext,
OnAutocompleteErrorCallback, OnAutocompleteErrorCallback,
ReturnOptionsTypes, ReturnOptionsTypes,
__TypesWrapper,
} from '..'; } from '..';
import type { Awaitable, FlatObjectKeys } from '../../common';
import type { ModalContext } from '../../components'; import type { ModalContext } from '../../components';
import type { ComponentContext } from '../../components/componentcontext'; import type { ComponentContext } from '../../components/componentcontext';
import type { MessageCommandInteraction, UserCommandInteraction } from '../../structures'; 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 { CommandContext } from './chatcontext';
import type { MiddlewareContext } from './shared'; import type { DefaultLocale, MiddlewareContext, OKFunction, StopFunction } from './shared';
export type SeyfertBasicOption<T extends keyof __TypesWrapper, D = object> = __TypesWrapper[T] & D; export interface SeyfertBasicOption<T extends keyof ReturnOptionsTypes> {
required?: boolean;
value?(
data: { context: CommandContext; value: ReturnOptionsTypes[T] },
ok: OKFunction<any>,
fail: StopFunction,
): Awaitable<void>;
description: string;
description_localizations?: APIApplicationCommandBasicOption['description_localizations'];
name_localizations?: APIApplicationCommandBasicOption['name_localizations'];
locales?: {
name?: FlatObjectKeys<DefaultLocale>;
description?: FlatObjectKeys<DefaultLocale>;
};
}
export type SeyfertStringOption = SeyfertBasicOption<'String'> & { export interface SeyfertBaseChoiceableOption<
T extends keyof ReturnOptionsTypes,
C = T extends ChoiceableTypes ? SeyfertChoice<ChoiceableValues[T]>[] : never,
> {
required?: boolean;
choices?: C;
value?: ValueCallback<T, C>;
description: string;
description_localizations?: APIApplicationCommandBasicOption['description_localizations'];
name_localizations?: APIApplicationCommandBasicOption['name_localizations'];
locales?: {
name?: FlatObjectKeys<DefaultLocale>;
description?: FlatObjectKeys<DefaultLocale>;
};
}
export type SeyfertChoice<T extends string | number> =
| { readonly name: string; readonly value: T }
| APIApplicationCommandOptionChoice<T>;
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<ChoiceableValues[T]>[] : never,
> = (
data: {
context: CommandContext;
value: T extends ChoiceableTypes
? C extends SeyfertChoice<ChoiceableValues[T]>[]
? C[number]['value'] extends ReturnOptionsTypes[T]
? C[number]['value']
: ReturnOptionsTypes[T]
: ReturnOptionsTypes[T]
: ReturnOptionsTypes[T];
},
ok: OKFunction<any>,
fail: StopFunction,
) => Awaitable<void>;
export type SeyfertStringOption<T = SeyfertChoice<string>[]> = SeyfertBaseChoiceableOption<
ApplicationCommandOptionType.String,
T
> & {
autocomplete?: AutocompleteCallback; autocomplete?: AutocompleteCallback;
onAutocompleteError?: OnAutocompleteErrorCallback; onAutocompleteError?: OnAutocompleteErrorCallback;
choices?:
| readonly { readonly name: string; readonly value: string }[]
| APIApplicationCommandOptionChoice<ReturnOptionsTypes[ApplicationCommandOptionType.String]>[];
min_length?: number; min_length?: number;
max_length?: number; max_length?: number;
}; };
export type SeyfertIntegerOption = SeyfertBasicOption<'Integer'> & { export type SeyfertIntegerOption<T = SeyfertChoice<number>[]> = SeyfertBaseChoiceableOption<
ApplicationCommandOptionType.Integer,
T
> & {
autocomplete?: AutocompleteCallback; autocomplete?: AutocompleteCallback;
onAutocompleteError?: OnAutocompleteErrorCallback; onAutocompleteError?: OnAutocompleteErrorCallback;
choices?: APIApplicationCommandOptionChoice<ReturnOptionsTypes[ApplicationCommandOptionType.Integer]>[];
min_value?: number; min_value?: number;
max_value?: number; max_value?: number;
}; };
export type SeyfertBooleanOption = SeyfertBasicOption<'Boolean'>; export type SeyfertNumberOption<T = SeyfertChoice<number>[]> = SeyfertBaseChoiceableOption<
export type SeyfertUserOption = SeyfertBasicOption<'User'>; ApplicationCommandOptionType.Number,
export type SeyfertChannelOption = SeyfertBasicOption<'Channel'> & { T
> & {
autocomplete?: AutocompleteCallback;
onAutocompleteError?: OnAutocompleteErrorCallback;
min_value?: number;
max_value?: number;
};
export type SeyfertBooleanOption = SeyfertBasicOption<ApplicationCommandOptionType.Boolean>;
export type SeyfertUserOption = SeyfertBasicOption<ApplicationCommandOptionType.User>;
export type SeyfertChannelOption = SeyfertBasicOption<ApplicationCommandOptionType.Channel> & {
channel_types?: ChannelType[]; channel_types?: ChannelType[];
}; };
export type SeyfertRoleOption = SeyfertBasicOption<'Role'>; export type SeyfertRoleOption = SeyfertBasicOption<ApplicationCommandOptionType.Role>;
export type SeyfertMentionableOption = SeyfertBasicOption<'Mentionable'>; export type SeyfertMentionableOption = SeyfertBasicOption<ApplicationCommandOptionType.Mentionable>;
export type SeyfertNumberOption = SeyfertBasicOption<'Number'> & { export type SeyfertAttachmentOption = SeyfertBasicOption<ApplicationCommandOptionType.Attachment>;
autocomplete?: AutocompleteCallback;
onAutocompleteError?: OnAutocompleteErrorCallback;
choices?: APIApplicationCommandOptionChoice<ReturnOptionsTypes[ApplicationCommandOptionType.Number]>[];
min_value?: number;
max_value?: number;
};
export type SeyfertAttachmentOption = SeyfertBasicOption<'Attachment'>;
export function createStringOption<T extends SeyfertStringOption = SeyfertStringOption>(data: T) { export function createStringOption<C extends SeyfertChoice<string>[] = SeyfertChoice<string>[]>(
data: SeyfertStringOption<C>,
) {
return { ...data, type: ApplicationCommandOptionType.String } as const; return { ...data, type: ApplicationCommandOptionType.String } as const;
} }
export function createIntegerOption<T extends SeyfertIntegerOption = SeyfertIntegerOption>(data: T) { export function createIntegerOption<C extends SeyfertChoice<number>[] = SeyfertChoice<number>[]>(
data: SeyfertIntegerOption<C>,
) {
return { ...data, type: ApplicationCommandOptionType.Integer } as const; return { ...data, type: ApplicationCommandOptionType.Integer } as const;
} }
export function createNumberOption<C extends SeyfertChoice<number>[] = SeyfertChoice<number>[]>(
data: SeyfertNumberOption<C>,
) {
return { ...data, type: ApplicationCommandOptionType.Number } as const;
}
export function createBooleanOption<T extends SeyfertBooleanOption = SeyfertBooleanOption>(data: T) { export function createBooleanOption<T extends SeyfertBooleanOption = SeyfertBooleanOption>(data: T) {
return { ...data, type: ApplicationCommandOptionType.Boolean } as const; return { ...data, type: ApplicationCommandOptionType.Boolean } as const;
} }
@ -75,10 +157,6 @@ export function createMentionableOption<T extends SeyfertMentionableOption = Sey
return { ...data, type: ApplicationCommandOptionType.Mentionable } as const; return { ...data, type: ApplicationCommandOptionType.Mentionable } as const;
} }
export function createNumberOption<T extends SeyfertNumberOption = SeyfertNumberOption>(data: T) {
return { ...data, type: ApplicationCommandOptionType.Number } as const;
}
export function createAttachmentOption<T extends SeyfertAttachmentOption = SeyfertAttachmentOption>(data: T) { export function createAttachmentOption<T extends SeyfertAttachmentOption = SeyfertAttachmentOption>(data: T) {
return { ...data, type: ApplicationCommandOptionType.Attachment } as const; return { ...data, type: ApplicationCommandOptionType.Attachment } as const;
} }