fix: type guard for sendable text-based channels (#10482)

* fix: type-guard for sendable text-based channels

* chore: suggested change

* Update packages/discord.js/typings/index.test-d.ts

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

* fix: make isSendable strictly check for `.send`

* fix: tests

---------

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
Vlad Frangu
2024-09-06 10:16:38 +03:00
committed by GitHub
parent c13f18e90e
commit dea68400a3
4 changed files with 46 additions and 2 deletions

View File

@@ -155,6 +155,14 @@ class BaseChannel extends Base {
return 'availableTags' in this; return 'availableTags' in this;
} }
/**
* Indicates whether this channel is sendable.
* @returns {boolean}
*/
isSendable() {
return 'send' in this;
}
toJSON(...props) { toJSON(...props) {
return super.toJSON({ createdTimestamp: true }, ...props); return super.toJSON({ createdTimestamp: true }, ...props);
} }

View File

@@ -117,9 +117,24 @@ exports.GuildTextBasedChannelTypes = [
* * {@link ChannelType.PrivateThread} * * {@link ChannelType.PrivateThread}
* * {@link ChannelType.GuildVoice} * * {@link ChannelType.GuildVoice}
* * {@link ChannelType.GuildStageVoice} * * {@link ChannelType.GuildStageVoice}
* * {@link ChannelType.GroupDM}
* @typedef {ChannelType[]} TextBasedChannelTypes * @typedef {ChannelType[]} TextBasedChannelTypes
*/ */
exports.TextBasedChannelTypes = [...exports.GuildTextBasedChannelTypes, ChannelType.DM]; exports.TextBasedChannelTypes = [...exports.GuildTextBasedChannelTypes, ChannelType.DM, ChannelType.GroupDM];
/**
* The types of channels that are text-based and can have messages sent into. The available types are:
* * {@link ChannelType.DM}
* * {@link ChannelType.GuildText}
* * {@link ChannelType.GuildAnnouncement}
* * {@link ChannelType.AnnouncementThread}
* * {@link ChannelType.PublicThread}
* * {@link ChannelType.PrivateThread}
* * {@link ChannelType.GuildVoice}
* * {@link ChannelType.GuildStageVoice}
* @typedef {ChannelType[]} SendableChannels
*/
exports.SendableChannels = [...exports.GuildTextBasedChannelTypes, ChannelType.DM];
/** /**
* The types of channels that are threads. The available types are: * The types of channels that are threads. The available types are:

View File

@@ -975,6 +975,7 @@ export abstract class BaseChannel extends Base {
public isDMBased(): this is PartialGroupDMChannel | DMChannel | PartialDMChannel; public isDMBased(): this is PartialGroupDMChannel | DMChannel | PartialDMChannel;
public isVoiceBased(): this is VoiceBasedChannel; public isVoiceBased(): this is VoiceBasedChannel;
public isThreadOnly(): this is ThreadOnlyChannel; public isThreadOnly(): this is ThreadOnlyChannel;
public isSendable(): this is SendableChannels;
public toString(): ChannelMention | UserMention; public toString(): ChannelMention | UserMention;
} }
@@ -3867,6 +3868,7 @@ export const Constants: {
SweeperKeys: SweeperKey[]; SweeperKeys: SweeperKey[];
NonSystemMessageTypes: NonSystemMessageType[]; NonSystemMessageTypes: NonSystemMessageType[];
TextBasedChannelTypes: TextBasedChannelTypes[]; TextBasedChannelTypes: TextBasedChannelTypes[];
SendableChannels: SendableChannelTypes[];
GuildTextBasedChannelTypes: GuildTextBasedChannelTypes[]; GuildTextBasedChannelTypes: GuildTextBasedChannelTypes[];
ThreadChannelTypes: ThreadChannelType[]; ThreadChannelTypes: ThreadChannelType[];
VoiceBasedChannelTypes: VoiceBasedChannelTypes[]; VoiceBasedChannelTypes: VoiceBasedChannelTypes[];
@@ -6879,11 +6881,15 @@ export type Channel =
export type TextBasedChannel = Exclude<Extract<Channel, { type: TextChannelType }>, ForumChannel | MediaChannel>; export type TextBasedChannel = Exclude<Extract<Channel, { type: TextChannelType }>, ForumChannel | MediaChannel>;
export type SendableChannels = Extract<Channel, { send: (...args: any[]) => any }>;
export type TextBasedChannels = TextBasedChannel; export type TextBasedChannels = TextBasedChannel;
export type TextBasedChannelTypes = TextBasedChannel['type']; export type TextBasedChannelTypes = TextBasedChannel['type'];
export type GuildTextBasedChannelTypes = Exclude<TextBasedChannelTypes, ChannelType.DM>; export type GuildTextBasedChannelTypes = Exclude<TextBasedChannelTypes, ChannelType.DM | ChannelType.GroupDM>;
export type SendableChannelTypes = SendableChannels['type'];
export type VoiceBasedChannel = Extract<Channel, { bitrate: number }>; export type VoiceBasedChannel = Extract<Channel, { bitrate: number }>;

View File

@@ -209,6 +209,7 @@ import {
ApplicationEmoji, ApplicationEmoji,
ApplicationEmojiManager, ApplicationEmojiManager,
StickerPack, StickerPack,
SendableChannels,
} from '.'; } from '.';
import { expectAssignable, expectDeprecated, expectNotAssignable, expectNotType, expectType } from 'tsd'; import { expectAssignable, expectDeprecated, expectNotAssignable, expectNotType, expectType } from 'tsd';
import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders'; import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders';
@@ -2593,3 +2594,17 @@ declare const poll: Poll;
expectType<Collection<Snowflake, StickerPack>>(await client.fetchStickerPacks()); expectType<Collection<Snowflake, StickerPack>>(await client.fetchStickerPacks());
expectType<Collection<Snowflake, StickerPack>>(await client.fetchStickerPacks({})); expectType<Collection<Snowflake, StickerPack>>(await client.fetchStickerPacks({}));
expectType<StickerPack>(await client.fetchStickerPacks({ packId: snowflake })); expectType<StickerPack>(await client.fetchStickerPacks({ packId: snowflake }));
client.on('interactionCreate', interaction => {
if (!interaction.channel) {
return;
}
// @ts-expect-error
interaction.channel.send();
if (interaction.channel.isSendable()) {
expectType<SendableChannels>(interaction.channel);
interaction.channel.send({ embeds: [] });
}
});