From b2836daafeac0da908c72938e7e8a79629569a69 Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni <77477100+suneettipirneni@users.noreply.github.com> Date: Wed, 27 Oct 2021 06:08:01 -0400 Subject: [PATCH] typings: cache types cleanup (#6867) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Almeida Co-authored-by: Antonio Román Co-authored-by: Vlad Frangu --- typings/index.d.ts | 260 ++++++++++++++++++--------------------------- typings/tests.ts | 42 +++++--- 2 files changed, 130 insertions(+), 172 deletions(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index 9f06685a8..78f44d1f8 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -268,15 +268,16 @@ export class BaseClient extends EventEmitter { public toJSON(...props: Record[]): unknown; } -export type GuildCacheMessage = CacheTypeReducer< +export type GuildCacheMessage = CacheTypeReducer< Cached, - GuildMessage & Message, + Message, APIMessage, Message | APIMessage, Message | APIMessage >; -export abstract class BaseCommandInteraction extends Interaction { +export abstract class BaseCommandInteraction extends Interaction { + public options: CommandInteractionOptionResolver; public readonly command: ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; public readonly channel: TextBasedChannels | null; public channelId: Snowflake; @@ -286,38 +287,26 @@ export abstract class BaseCommandInteraction extends Interaction { public ephemeral: boolean | null; public replied: boolean; public webhook: InteractionWebhook; - public inGuild(): this is InteractionResponses<'present'> & this; - public inCachedGuild(): this is InteractionResponses<'cached'> & this; - public inRawGuild(): this is InteractionResponses<'raw'> & this; - public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise; + public inGuild(): this is BaseCommandInteraction<'present'> & this; + public inCachedGuild(): this is BaseCommandInteraction<'cached'> & this; + public inRawGuild(): this is BaseCommandInteraction<'raw'> & this; + public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise>; public deferReply(options?: InteractionDeferReplyOptions): Promise; public deleteReply(): Promise; - public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise; - public fetchReply(): Promise; - public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise; - public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise; + public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise>; + public fetchReply(): Promise>; + public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise>; + public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise>; public reply(options: string | MessagePayload | InteractionReplyOptions): Promise; private transformOption( option: APIApplicationCommandOption, resolved: APIApplicationCommandInteractionData['resolved'], - ): CommandInteractionOption; - private transformResolved(resolved: APIApplicationCommandInteractionData['resolved']): CommandInteractionResolvedData; + ): CommandInteractionOption; + private transformResolved( + resolved: APIApplicationCommandInteractionData['resolved'], + ): CommandInteractionResolvedData; } -export interface InteractionResponsesResolvable { - inGuild(): this is InteractionResponses<'present'> & this; - inCachedGuild(): this is InteractionResponses<'cached'> & this; - inRawGuild(): this is InteractionResponses<'raw'> & this; -} - -export type CacheHelper< - T extends Interaction, - Cached extends GuildCacheState, -> = T extends InteractionResponsesResolvable ? InteractionResponses & T : GuildInteraction & T; - -export type CachedInteraction = CacheHelper; -export type RawInteraction = CacheHelper; -export type PresentInteraction = CacheHelper; export abstract class BaseGuild extends Base { protected constructor(client: Client, data: RawBaseGuildData); public readonly createdAt: Date; @@ -405,7 +394,7 @@ export class BitField { public static resolve(bit?: BitFieldResolvable): number | bigint; } -export class ButtonInteraction extends MessageComponentInteraction { +export class ButtonInteraction extends MessageComponentInteraction { private constructor(client: Client, data: RawMessageButtonInteractionData); public componentType: 'BUTTON'; } @@ -601,39 +590,33 @@ export abstract class Collector extends EventEmi public once(event: 'end', listener: (collected: Collection, reason: string) => Awaitable): this; } -export type GuildCommandInteraction = InteractionResponses & - CommandInteraction; - -export class CommandInteraction extends BaseCommandInteraction { - public options: CommandInteractionOptionResolver; - public inCachedGuild(): this is GuildCommandInteraction<'cached'> & this; - public inRawGuild(): this is GuildCommandInteraction<'raw'> & this; +export class CommandInteraction extends BaseCommandInteraction { public toString(): string; } -export class CommandInteractionOptionResolver { +export class CommandInteractionOptionResolver { private constructor(client: Client, options: CommandInteractionOption[], resolved: CommandInteractionResolvedData); public readonly client: Client; - public readonly data: readonly CommandInteractionOption[]; - public readonly resolved: Readonly; + public readonly data: readonly CommandInteractionOption[]; + public readonly resolved: Readonly>; private _group: string | null; - private _hoistedOptions: CommandInteractionOption[]; + private _hoistedOptions: CommandInteractionOption[]; private _subcommand: string | null; private _getTypedOption( name: string, type: ApplicationCommandOptionType, properties: (keyof ApplicationCommandOption)[], required: true, - ): CommandInteractionOption; + ): CommandInteractionOption; private _getTypedOption( name: string, type: ApplicationCommandOptionType, properties: (keyof ApplicationCommandOption)[], required: boolean, - ): CommandInteractionOption | null; + ): CommandInteractionOption | null; - public get(name: string, required: true): CommandInteractionOption; - public get(name: string, required?: boolean): CommandInteractionOption | null; + public get(name: string, required: true): CommandInteractionOption; + public get(name: string, required?: boolean): CommandInteractionOption | null; public getSubcommand(required?: true): string; public getSubcommand(required: boolean): string | null; @@ -641,42 +624,35 @@ export class CommandInteractionOptionResolver; - public getChannel(name: string, required?: boolean): NonNullable | null; + public getChannel(name: string, required: true): NonNullable['channel']>; + public getChannel(name: string, required?: boolean): NonNullable['channel']> | null; public getString(name: string, required: true): string; public getString(name: string, required?: boolean): string | null; public getInteger(name: string, required: true): number; public getInteger(name: string, required?: boolean): number | null; public getNumber(name: string, required: true): number; public getNumber(name: string, required?: boolean): number | null; - public getUser(name: string, required: true): NonNullable; - public getUser(name: string, required?: boolean): NonNullable | null; - public getMember( - name: string, - required: true, - ): CacheTypeReducer>; - public getMember( - name: string, - required?: boolean, - ): CacheTypeReducer> | null; - public getRole(name: string, required: true): NonNullable; - public getRole(name: string, required?: boolean): NonNullable | null; + public getUser(name: string, required: true): NonNullable['user']>; + public getUser(name: string, required?: boolean): NonNullable['user']> | null; + public getMember(name: string, required: true): NonNullable['member']>; + public getMember(name: string, required?: boolean): NonNullable['member']> | null; + public getRole(name: string, required: true): NonNullable['role']>; + public getRole(name: string, required?: boolean): NonNullable['role']> | null; public getMentionable( name: string, required: true, - ): NonNullable; + ): NonNullable['member' | 'role' | 'user']>; public getMentionable( name: string, required?: boolean, - ): NonNullable | null; - public getMessage(name: string, required: true): NonNullable; - public getMessage(name: string, required?: boolean): NonNullable | null; + ): NonNullable['member' | 'role' | 'user']> | null; + public getMessage(name: string, required: true): NonNullable['message']>; + public getMessage(name: string, required?: boolean): NonNullable['message']> | null; } -export class ContextMenuInteraction extends BaseCommandInteraction { - public options: CommandInteractionOptionResolver; +export class ContextMenuInteraction extends BaseCommandInteraction { public targetId: Snowflake; public targetType: Exclude; - private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption[]; + private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption[]; } export class DataResolver extends null { @@ -1080,10 +1056,10 @@ export class Intents extends BitField { public static resolve(bit?: BitFieldResolvable): number; } -export type GuildCacheState = 'cached' | 'raw' | 'present'; +export type CacheType = 'cached' | 'raw' | 'present'; export type CacheTypeReducer< - State extends GuildCacheState, + State extends CacheType, CachedType, RawType = CachedType, PresentType = CachedType | RawType, @@ -1096,51 +1072,33 @@ export type CacheTypeReducer< ? PresentType : Fallback; -export interface GuildInteraction extends Interaction { - guildId: Snowflake; - member: CacheTypeReducer; - readonly guild: CacheTypeReducer; - channel: CacheTypeReducer | null>; - isCommand(): this is GuildCommandInteraction & this; -} - -export class Interaction extends Base { +export class Interaction extends Base { + // This a technique used to brand different cached types. Or else we'll get `never` errors on typeguard checks. + private readonly _cacheType: Cached; protected constructor(client: Client, data: RawInteractionData); public applicationId: Snowflake; - public readonly channel: TextBasedChannels | null; + public channel: CacheTypeReducer; public channelId: Snowflake | null; public readonly createdAt: Date; public readonly createdTimestamp: number; - public readonly guild: Guild | null; - public guildId: Snowflake | null; + public readonly guild: CacheTypeReducer; + public guildId: CacheTypeReducer; public id: Snowflake; - public member: GuildMember | APIInteractionGuildMember | null; + public member: CacheTypeReducer; public readonly token: string; public type: InteractionType; public user: User; public version: number; public memberPermissions: Readonly | null; - public inGuild(): this is GuildInteraction<'present'> & this; - public inCachedGuild(): this is GuildInteraction<'cached'> & this; - public inRawGuild(): this is GuildInteraction<'raw'> & this; - public isApplicationCommand(): this is BaseCommandInteraction; - public isButton(): this is ButtonInteraction; - public isCommand(): this is CommandInteraction; - public isContextMenu(): this is ContextMenuInteraction; - public isMessageComponent(): this is MessageComponentInteraction; - public isSelectMenu(): this is SelectMenuInteraction; -} - -export interface InteractionResponses - extends GuildInteraction { - deferReply(options?: InteractionDeferReplyOptions): Promise; - deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise>; - editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise>; - deleteReply(): Promise; - fetchReply(): Promise>; - reply(options: InteractionReplyOptions & { fetchReply: true }): Promise>; - reply(options: string | MessagePayload | InteractionReplyOptions): Promise; - followUp(options: string | MessagePayload | InteractionReplyOptions): Promise>; + public inGuild(): this is Interaction<'present'> & this; + public inCachedGuild(): this is Interaction<'cached'> & this; + public inRawGuild(): this is Interaction<'raw'> & this; + public isApplicationCommand(): this is BaseCommandInteraction; + public isButton(): this is ButtonInteraction; + public isCommand(): this is CommandInteraction; + public isContextMenu(): this is ContextMenuInteraction; + public isMessageComponent(): this is MessageComponentInteraction; + public isSelectMenu(): this is SelectMenuInteraction; } export class InteractionCollector extends Collector { @@ -1268,10 +1226,11 @@ export type InteractionCollectorReturnType< ? ConditionalInteractionCollectorType[T]> : InteractionCollector; -export type InteractionExtractor = T extends - | MessageComponentType - | MessageComponentTypes - ? MappedInteractionCollectorOptions[T] extends InteractionCollectorOptions +export type InteractionExtractor< + T extends MessageComponentType | MessageComponentTypes | undefined, + C extends boolean = false, +> = T extends MessageComponentType | MessageComponentTypes + ? MappedInteractionCollectorOptions[T] extends InteractionCollectorOptions ? Item : never : MessageComponentInteraction; @@ -1289,22 +1248,8 @@ export type AwaitMessageCollectorOptionsParams; -export type CachedMessage = GuildMessage<'cached'> & Message; -export interface GuildMessage { - awaitMessageComponent< - T extends MessageComponentType | MessageComponentTypes | undefined = MessageComponentTypes.ACTION_ROW, - >( - options?: AwaitMessageCollectorOptionsParams, - ): Promise & InteractionExtractor>; - - createMessageComponentCollector( - options?: MessageCollectorOptionsParams, - ): InteractionCollectorReturnType; - - readonly channel: CacheTypeReducer; -} - -export class Message extends Base { +export class Message extends Base { + private readonly _cacheType: Cached; private constructor(client: Client, data: RawMessageData); private _patch(data: RawPartialMessageData | RawMessageData): void; @@ -1312,7 +1257,7 @@ export class Message extends Base { public applicationId: Snowflake | null; public attachments: Collection; public author: User; - public readonly channel: TextBasedChannels; + public readonly channel: If; public channelId: Snowflake; public readonly cleanContent: string; public components: MessageActionRow[]; @@ -1350,12 +1295,12 @@ export class Message extends Base { public reference: MessageReference | null; public awaitMessageComponent< T extends MessageComponentType | MessageComponentTypes | undefined = MessageComponentTypes.ACTION_ROW, - >(options?: AwaitMessageCollectorOptionsParams): Promise>; + >(options?: AwaitMessageCollectorOptionsParams): Promise>; public awaitReactions(options?: AwaitReactionsOptions): Promise>; public createReactionCollector(options?: ReactionCollectorOptions): ReactionCollector; public createMessageComponentCollector< T extends MessageComponentType | MessageComponentTypes | undefined = undefined, - >(options?: MessageCollectorOptionsParams): InteractionCollectorReturnType; + >(options?: MessageCollectorOptionsParams): InteractionCollectorReturnType; public delete(): Promise; public edit(content: string | MessageEditOptions | MessagePayload): Promise; public equals(message: Message, rawData: unknown): boolean; @@ -1373,7 +1318,7 @@ export class Message extends Base { public toJSON(): unknown; public toString(): string; public unpin(): Promise; - public inGuild(): this is GuildMessage<'cached'> & this; + public inGuild(): this is Message & this; } export class MessageActionRow extends BaseMessageComponent { @@ -1447,32 +1392,36 @@ export class MessageCollector extends Collector { public dispose(message: Message): Snowflake | null; } -export class MessageComponentInteraction extends Interaction { +export class MessageComponentInteraction extends Interaction { protected constructor(client: Client, data: RawMessageComponentInteractionData); - public readonly channel: TextBasedChannels | null; - public readonly component: MessageActionRowComponent | Exclude; + public readonly channel: CacheTypeReducer; + public readonly component: CacheTypeReducer< + Cached, + MessageActionRowComponent, + Exclude + > | null; public componentType: Exclude; public customId: string; public channelId: Snowflake; public deferred: boolean; public ephemeral: boolean | null; - public message: Message | APIMessage; + public message: CacheTypeReducer; public replied: boolean; public webhook: InteractionWebhook; - public inGuild(): this is InteractionResponses<'present'> & this; - public inCachedGuild(): this is InteractionResponses<'cached'> & this; - public inRawGuild(): this is InteractionResponses<'raw'> & this; - public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise; + public inGuild(): this is MessageComponentInteraction<'present'> & this; + public inCachedGuild(): this is MessageComponentInteraction<'cached'> & this; + public inRawGuild(): this is MessageComponentInteraction<'raw'> & this; + public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise>; public deferReply(options?: InteractionDeferReplyOptions): Promise; - public deferUpdate(options: InteractionDeferUpdateOptions & { fetchReply: true }): Promise; + public deferUpdate(options: InteractionDeferUpdateOptions & { fetchReply: true }): Promise>; public deferUpdate(options?: InteractionDeferUpdateOptions): Promise; public deleteReply(): Promise; - public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise; - public fetchReply(): Promise; - public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise; - public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise; + public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise>; + public fetchReply(): Promise>; + public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise>; + public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise>; public reply(options: string | MessagePayload | InteractionReplyOptions): Promise; - public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise; + public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise>; public update(options: string | MessagePayload | InteractionUpdateOptions): Promise; public static resolveType(type: MessageComponentTypeResolvable): MessageComponentType; @@ -1767,7 +1716,7 @@ export class Role extends Base { public static comparePositions(role1: Role, role2: Role): number; } -export class SelectMenuInteraction extends MessageComponentInteraction { +export class SelectMenuInteraction extends MessageComponentInteraction { public constructor(client: Client, data: RawMessageSelectMenuInteractionData); public componentType: 'SELECT_MENU'; public values: string[]; @@ -3681,24 +3630,24 @@ export type ColorResolvable = | number | HexColorString; -export interface CommandInteractionOption { +export interface CommandInteractionOption { name: string; type: ApplicationCommandOptionType; value?: string | number | boolean; options?: CommandInteractionOption[]; user?: User; - member?: GuildMember | APIInteractionDataResolvedGuildMember; - channel?: GuildChannel | ThreadChannel | APIInteractionDataResolvedChannel; - role?: Role | APIRole; - message?: Message | APIMessage; + member?: CacheTypeReducer; + channel?: CacheTypeReducer; + role?: CacheTypeReducer; + message?: CacheTypeReducer; } -export interface CommandInteractionResolvedData { +export interface CommandInteractionResolvedData { users?: Collection; - members?: Collection; - roles?: Collection; - channels?: Collection; - messages?: Collection; + members?: Collection>; + roles?: Collection>; + channels?: Collection>; + messages?: Collection>; } export interface ConstantsClientApplicationAssetTypes { @@ -4263,7 +4212,8 @@ export interface IntegrationAccount { export type IntegrationType = 'twitch' | 'youtube' | 'discord'; -export interface InteractionCollectorOptions extends CollectorOptions<[T]> { +export interface InteractionCollectorOptions + extends CollectorOptions<[T]> { channel?: TextBasedChannels; componentType?: MessageComponentType | MessageComponentTypes; guild?: Guild; @@ -4271,26 +4221,24 @@ export interface InteractionCollectorOptions extends Coll max?: number; maxComponents?: number; maxUsers?: number; - message?: Message | APIMessage; + message?: CacheTypeReducer; } export interface ButtonInteractionCollectorOptions - extends MessageComponentCollectorOptions< - Cached extends true ? CachedInteraction : ButtonInteraction - > { + extends MessageComponentCollectorOptions : ButtonInteraction> { componentType: 'BUTTON' | MessageComponentTypes.BUTTON; } export interface SelectMenuInteractionCollectorOptions extends MessageComponentCollectorOptions< - Cached extends true ? CachedInteraction : SelectMenuInteraction + Cached extends true ? SelectMenuInteraction<'cached'> : SelectMenuInteraction > { componentType: 'SELECT_MENU' | MessageComponentTypes.SELECT_MENU; } export interface MessageInteractionCollectorOptions extends MessageComponentCollectorOptions< - Cached extends true ? CachedInteraction : MessageComponentInteraction + Cached extends true ? MessageComponentInteraction<'cached'> : MessageComponentInteraction > { componentType: 'ACTION_ROW' | MessageComponentTypes.ACTION_ROW; } diff --git a/typings/tests.ts b/typings/tests.ts index 6f33f7d80..a1be4794c 100644 --- a/typings/tests.ts +++ b/typings/tests.ts @@ -5,6 +5,8 @@ import { APIPartialChannel, APIPartialGuild, APIInteractionDataResolvedGuildMember, + APIInteractionDataResolvedChannel, + APIRole, } from 'discord-api-types/v9'; import { ApplicationCommand, @@ -33,19 +35,17 @@ import { DMChannel, Guild, GuildApplicationCommandManager, - CachedInteraction, + GuildChannel, GuildChannelManager, GuildEmoji, GuildEmojiManager, GuildMember, - GuildMessage, GuildResolvable, GuildTextBasedChannel, GuildTextChannelResolvable, Intents, Interaction, InteractionCollector, - InteractionResponses, LimitedCollection, Message, MessageActionRow, @@ -77,7 +77,6 @@ import { Typing, User, VoiceChannel, - CachedMessage, } from '.'; import { ApplicationCommandOptionTypes } from './enums'; @@ -498,13 +497,13 @@ client.on('messageCreate', async message => { assertIsMessage(channel.send({ embeds: [embed], files: [attachment] })); if (message.inGuild()) { - assertType(message); + assertType>(message); const component = await message.awaitMessageComponent({ componentType: 'BUTTON' }); - assertType>(component); - assertType>(component.reply({ fetchReply: true })); + assertType>(component); + assertType>(await component.reply({ fetchReply: true })); const buttonCollector = message.createMessageComponentCollector({ componentType: 'BUTTON' }); - assertType>>(buttonCollector); + assertType>>(buttonCollector); assertType(message.channel); } @@ -897,14 +896,11 @@ declare const booleanValue: boolean; if (interaction.inGuild()) assertType(interaction.guildId); client.on('interactionCreate', async interaction => { - const consumeCachedCommand = (_i: CachedInteraction) => {}; - const consumeCachedInteraction = (_i: CachedInteraction) => {}; - if (interaction.inCachedGuild()) { assertType(interaction.member); // @ts-expect-error - consumeCachedCommand(interaction); - consumeCachedInteraction(interaction); + assertType>(interaction); + assertType(interaction); } else if (interaction.inRawGuild()) { assertType(interaction.member); // @ts-expect-error @@ -920,7 +916,7 @@ client.on('interactionCreate', async interaction => { if (interaction.inCachedGuild()) { assertType(interaction); assertType(interaction.guild); - consumeCachedCommand(interaction); + assertType>(interaction); } else if (interaction.inRawGuild()) { assertType(interaction); assertType(interaction.guild); @@ -989,18 +985,26 @@ client.on('interactionCreate', async interaction => { assertType>(interaction.reply({ fetchReply: true })); assertType(interaction.options.getMember('test')); assertType(interaction.options.getMember('test', true)); + + assertType(interaction.options.getChannel('test', true)); + assertType(interaction.options.getRole('test', true)); + assertType(interaction.options.getMessage('test', true)); } else if (interaction.inCachedGuild()) { const msg = await interaction.reply({ fetchReply: true }); const btn = await msg.awaitMessageComponent({ componentType: 'BUTTON' }); assertType(msg); - assertType>(btn); + assertType>(btn); - consumeCachedCommand(interaction); + assertType>(interaction); assertType(interaction.options.getMember('test', true)); assertType(interaction.options.getMember('test')); assertType(interaction); assertType>(interaction.reply({ fetchReply: true })); + + assertType(interaction.options.getChannel('test', true)); + assertType(interaction.options.getRole('test', true)); + assertType(interaction.options.getMessage('test', true)); } else { // @ts-expect-error consumeCachedCommand(interaction); @@ -1008,6 +1012,12 @@ client.on('interactionCreate', async interaction => { assertType>(interaction.reply({ fetchReply: true })); assertType(interaction.options.getMember('test')); assertType(interaction.options.getMember('test', true)); + + assertType( + interaction.options.getChannel('test', true), + ); + assertType(interaction.options.getRole('test', true)); + assertType(interaction.options.getMessage('test', true)); } assertType(interaction);