feat(Client): add conditional ready typings (#6073)

This commit is contained in:
Shino
2021-07-14 11:19:38 -04:00
committed by GitHub
parent 60148c6a78
commit 4206e35b23
4 changed files with 46 additions and 10 deletions

View File

@@ -18,7 +18,7 @@ const VoiceRegion = require('../structures/VoiceRegion');
const Webhook = require('../structures/Webhook'); const Webhook = require('../structures/Webhook');
const Widget = require('../structures/Widget'); const Widget = require('../structures/Widget');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
const { Events, InviteScopes } = require('../util/Constants'); const { Events, InviteScopes, Status } = require('../util/Constants');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
const Intents = require('../util/Intents'); const Intents = require('../util/Intents');
const Options = require('../util/Options'); 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. * Logs out, terminates the connection to Discord, and destroys the client.
* @returns {void} * @returns {void}

View File

@@ -373,8 +373,9 @@ class WebSocketManager extends EventEmitter {
/** /**
* Emitted when the client becomes ready to start working. * Emitted when the client becomes ready to start working.
* @event Client#ready * @event Client#ready
* @param {Client} client The client
*/ */
this.client.emit(Events.CLIENT_READY); this.client.emit(Events.CLIENT_READY, this.client);
this.handlePacket(); this.handlePacket();
} }

19
typings/index.d.ts vendored
View File

@@ -278,23 +278,25 @@ export class Channel extends Base {
public toString(): ChannelMention; public toString(): ChannelMention;
} }
export class Client extends BaseClient { type If<T extends boolean, A, B = null> = T extends true ? A : T extends false ? B : A | B;
export class Client<Ready extends boolean = boolean> extends BaseClient {
public constructor(options: ClientOptions); public constructor(options: ClientOptions);
private actions: unknown; private actions: unknown;
private _eval(script: string): unknown; private _eval(script: string): unknown;
private _validateOptions(options: ClientOptions): void; private _validateOptions(options: ClientOptions): void;
public application: ClientApplication | null; public application: If<Ready, ClientApplication>;
public channels: ChannelManager; public channels: ChannelManager;
public readonly emojis: BaseGuildEmojiManager; public readonly emojis: BaseGuildEmojiManager;
public guilds: GuildManager; public guilds: GuildManager;
public options: ClientOptions; public options: ClientOptions;
public readyAt: Date | null; public readyAt: If<Ready, Date>;
public readonly readyTimestamp: number | null; public readonly readyTimestamp: If<Ready, number>;
public shard: ShardClientUtil | null; public shard: ShardClientUtil | null;
public token: string | null; public token: If<Ready, string, string | null>;
public readonly uptime: number | null; public uptime: If<Ready, number>;
public user: ClientUser | null; public user: If<Ready, ClientUser>;
public users: UserManager; public users: UserManager;
public voice: ClientVoiceManager; public voice: ClientVoiceManager;
public ws: WebSocketManager; public ws: WebSocketManager;
@@ -307,6 +309,7 @@ export class Client extends BaseClient {
public fetchWidget(guild: GuildResolvable): Promise<Widget>; public fetchWidget(guild: GuildResolvable): Promise<Widget>;
public generateInvite(options?: InviteGenerationOptions): string; public generateInvite(options?: InviteGenerationOptions): string;
public login(token?: string): Promise<string>; public login(token?: string): Promise<string>;
public isReady(): this is Client<true>;
public sweepMessages(lifetime?: number): number; public sweepMessages(lifetime?: number): number;
public toJSON(): unknown; public toJSON(): unknown;
@@ -2818,7 +2821,7 @@ export interface ClientEvents {
presenceUpdate: [oldPresence: Presence | null, newPresence: Presence]; presenceUpdate: [oldPresence: Presence | null, newPresence: Presence];
rateLimit: [rateLimitData: RateLimitData]; rateLimit: [rateLimitData: RateLimitData];
invalidRequestWarning: [invalidRequestWarningData: InvalidRequestWarningData]; invalidRequestWarning: [invalidRequestWarningData: InvalidRequestWarningData];
ready: []; ready: [client: Client<true>];
invalidated: []; invalidated: [];
roleCreate: [role: Role]; roleCreate: [role: Role];
roleDelete: [role: Role]; roleDelete: [role: Role];

View File

@@ -5,6 +5,8 @@ import {
ApplicationCommandResolvable, ApplicationCommandResolvable,
CategoryChannel, CategoryChannel,
Client, Client,
ClientApplication,
ClientUser,
Collection, Collection,
Constants, Constants,
DMChannel, DMChannel,
@@ -447,6 +449,27 @@ client.on('interaction', async interaction => {
client.login('absolutely-valid-token'); client.login('absolutely-valid-token');
// Test client conditional types
client.on('ready', client => {
assertType<Client<true>>(client);
});
declare const loggedInClient: Client<true>;
assertType<ClientApplication>(loggedInClient.application);
assertType<Date>(loggedInClient.readyAt);
assertType<number>(loggedInClient.readyTimestamp);
assertType<string>(loggedInClient.token);
assertType<number>(loggedInClient.uptime);
assertType<ClientUser>(loggedInClient.user);
declare const loggedOutClient: Client<false>;
assertType<null>(loggedOutClient.application);
assertType<null>(loggedOutClient.readyAt);
assertType<null>(loggedOutClient.readyTimestamp);
assertType<string | null>(loggedOutClient.token);
assertType<null>(loggedOutClient.uptime);
assertType<null>(loggedOutClient.user);
// Test type transformation: // Test type transformation:
declare const assertType: <T>(value: T) => asserts value is T; declare const assertType: <T>(value: T) => asserts value is T;
declare const serialize: <T>(value: T) => Serialized<T>; declare const serialize: <T>(value: T) => Serialized<T>;