diff --git a/src/client/Client.js b/src/client/Client.js index 3079c3b34f26..09bee5357efd 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -18,7 +18,7 @@ const VoiceRegion = require('../structures/VoiceRegion'); const Webhook = require('../structures/Webhook'); const Widget = require('../structures/Widget'); const Collection = require('../util/Collection'); -const { Events, InviteScopes } = require('../util/Constants'); +const { Events, InviteScopes, Status } = require('../util/Constants'); const DataResolver = require('../util/DataResolver'); const Intents = require('../util/Intents'); const Options = require('../util/Options'); @@ -227,6 +227,15 @@ class Client extends BaseClient { } } + /** + * Returns whether the client has logged in, indicative of being able to access + * properties such as `user` and `application`. + * @returns {boolean} + */ + isReady() { + return this.ws.status === Status.READY; + } + /** * Logs out, terminates the connection to Discord, and destroys the client. * @returns {void} diff --git a/src/client/websocket/WebSocketManager.js b/src/client/websocket/WebSocketManager.js index b01975279a9b..945ba340338f 100644 --- a/src/client/websocket/WebSocketManager.js +++ b/src/client/websocket/WebSocketManager.js @@ -373,8 +373,9 @@ class WebSocketManager extends EventEmitter { /** * Emitted when the client becomes ready to start working. * @event Client#ready + * @param {Client} client The client */ - this.client.emit(Events.CLIENT_READY); + this.client.emit(Events.CLIENT_READY, this.client); this.handlePacket(); } diff --git a/typings/index.d.ts b/typings/index.d.ts index f1e26d067610..c8caf4dee5af 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -278,23 +278,25 @@ export class Channel extends Base { public toString(): ChannelMention; } -export class Client extends BaseClient { +type If = T extends true ? A : T extends false ? B : A | B; + +export class Client extends BaseClient { public constructor(options: ClientOptions); private actions: unknown; private _eval(script: string): unknown; private _validateOptions(options: ClientOptions): void; - public application: ClientApplication | null; + public application: If; public channels: ChannelManager; public readonly emojis: BaseGuildEmojiManager; public guilds: GuildManager; public options: ClientOptions; - public readyAt: Date | null; - public readonly readyTimestamp: number | null; + public readyAt: If; + public readonly readyTimestamp: If; public shard: ShardClientUtil | null; - public token: string | null; - public readonly uptime: number | null; - public user: ClientUser | null; + public token: If; + public uptime: If; + public user: If; public users: UserManager; public voice: ClientVoiceManager; public ws: WebSocketManager; @@ -307,6 +309,7 @@ export class Client extends BaseClient { public fetchWidget(guild: GuildResolvable): Promise; public generateInvite(options?: InviteGenerationOptions): string; public login(token?: string): Promise; + public isReady(): this is Client; public sweepMessages(lifetime?: number): number; public toJSON(): unknown; @@ -2818,7 +2821,7 @@ export interface ClientEvents { presenceUpdate: [oldPresence: Presence | null, newPresence: Presence]; rateLimit: [rateLimitData: RateLimitData]; invalidRequestWarning: [invalidRequestWarningData: InvalidRequestWarningData]; - ready: []; + ready: [client: Client]; invalidated: []; roleCreate: [role: Role]; roleDelete: [role: Role]; diff --git a/typings/index.ts b/typings/index.ts index 2f735626f2e0..17a2af10fb2c 100644 --- a/typings/index.ts +++ b/typings/index.ts @@ -5,6 +5,8 @@ import { ApplicationCommandResolvable, CategoryChannel, Client, + ClientApplication, + ClientUser, Collection, Constants, DMChannel, @@ -447,6 +449,27 @@ client.on('interaction', async interaction => { client.login('absolutely-valid-token'); +// Test client conditional types +client.on('ready', client => { + assertType>(client); +}); + +declare const loggedInClient: Client; +assertType(loggedInClient.application); +assertType(loggedInClient.readyAt); +assertType(loggedInClient.readyTimestamp); +assertType(loggedInClient.token); +assertType(loggedInClient.uptime); +assertType(loggedInClient.user); + +declare const loggedOutClient: Client; +assertType(loggedOutClient.application); +assertType(loggedOutClient.readyAt); +assertType(loggedOutClient.readyTimestamp); +assertType(loggedOutClient.token); +assertType(loggedOutClient.uptime); +assertType(loggedOutClient.user); + // Test type transformation: declare const assertType: (value: T) => asserts value is T; declare const serialize: (value: T) => Serialized;