Skip to content

Commit

Permalink
fix: plugin types compatibility issue with ctx.has (#268)
Browse files Browse the repository at this point in the history
Co-authored-by: Kirill Loskutov <[email protected]>
Co-authored-by: KnorpelSenf <[email protected]>
  • Loading branch information
Loskir and KnorpelSenf committed Aug 28, 2022
1 parent bd13a27 commit e9ae05b
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 15 deletions.
46 changes: 34 additions & 12 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// deno-lint-ignore-file camelcase
import { type Api, type Other as OtherApi } from "./core/api.ts";
import { type Methods, type RawApi } from "./core/client.ts";
import { type Filter, type FilterQuery, matchFilter } from "./filter.ts";
import {
type Filter,
type FilterCore,
type FilterQuery,
matchFilter,
} from "./filter.ts";
import {
type Chat,
type ChatPermissions,
Expand Down Expand Up @@ -412,7 +417,7 @@ export class Context implements RenamedUpdate {
*
* @param filter The filter query to check
*/
has<Q extends FilterQuery>(filter: Q | Q[]): this is Filter<this, Q> {
has<Q extends FilterQuery>(filter: Q | Q[]): this is FilterCore<Q> {
return Context.has.filterQuery(filter)(this);
}
/**
Expand All @@ -422,7 +427,7 @@ export class Context implements RenamedUpdate {
*
* @param trigger The string or regex to match
*/
hasText(trigger: MaybeArray<string | RegExp>): this is HearsContext<this> {
hasText(trigger: MaybeArray<string | RegExp>): this is HearsContextCore {
return Context.has.text(trigger)(this);
}
/**
Expand All @@ -435,7 +440,7 @@ export class Context implements RenamedUpdate {
command: MaybeArray<
StringWithSuggestions<S | "start" | "help" | "settings">
>,
): this is CommandContext<this> {
): this is CommandContextCore {
return Context.has.command(command)(this);
}
/**
Expand All @@ -447,7 +452,7 @@ export class Context implements RenamedUpdate {
*/
hasChatType<T extends Chat["type"]>(
chatType: MaybeArray<T>,
): this is ChatTypeContext<this, T> {
): this is ChatTypeContextCore<T> {
return Context.has.chatType(chatType)(this);
}
/**
Expand All @@ -460,7 +465,7 @@ export class Context implements RenamedUpdate {
*/
hasCallbackQuery(
trigger: MaybeArray<string | RegExp>,
): this is CallbackQueryContext<this> {
): this is CallbackQueryContextCore {
return Context.has.callbackQuery(trigger)(this);
}
/**
Expand All @@ -472,7 +477,7 @@ export class Context implements RenamedUpdate {
*/
hasGameQuery(
trigger: MaybeArray<string | RegExp>,
): this is GameQueryContext<this> {
): this is GameQueryContextCore {
return Context.has.gameQuery(trigger)(this);
}
/**
Expand All @@ -484,7 +489,7 @@ export class Context implements RenamedUpdate {
*/
hasInlineQuery(
trigger: MaybeArray<string | RegExp>,
): this is InlineQueryContext<this> {
): this is InlineQueryContextCore {
return Context.has.inlineQuery(trigger)(this);
}

Expand Down Expand Up @@ -2039,6 +2044,9 @@ export class Context implements RenamedUpdate {
}

// === Filtered context types
type HearsContextCore =
& FilterCore<":text" | ":caption">
& NarrowMatchCore<string | RegExpMatchArray>;
/**
* Type of the context object that is available inside the handlers for
* `bot.hears`.
Expand All @@ -2053,6 +2061,10 @@ export type HearsContext<C extends Context> = Filter<
NarrowMatch<C, string | RegExpMatchArray>,
":text" | ":caption"
>;

type CommandContextCore =
& FilterCore<":entities:bot_command">
& NarrowMatchCore<string>;
/**
* Type of the context object that is available inside the handlers for
* `bot.command`.
Expand All @@ -2067,9 +2079,12 @@ export type CommandContext<C extends Context> = Filter<
NarrowMatch<C, string>,
":entities:bot_command"
>;
type NarrowMatchCore<T extends Context["match"]> = { match: T };
type NarrowMatch<C extends Context, T extends C["match"]> = {
[K in keyof C]: K extends "match" ? (T extends C[K] ? T : never) : C[K];
};

type CallbackQueryContextCore = FilterCore<"callback_query:data">;
/**
* Type of the context object that is available inside the handlers for
* `bot.callbackQuery`.
Expand All @@ -2084,6 +2099,8 @@ export type CallbackQueryContext<C extends Context> = Filter<
C,
"callback_query:data"
>;

type GameQueryContextCore = FilterCore<"callback_query:game_short_name">;
/**
* Type of the context object that is available inside the handlers for
* `bot.gameQuery`.
Expand All @@ -2098,6 +2115,8 @@ export type GameQueryContext<C extends Context> = Filter<
C,
"callback_query:game_short_name"
>;

type InlineQueryContextCore = FilterCore<"inline_query">;
/**
* Type of the context object that is available inside the handlers for
* `bot.inlineQuery`.
Expand All @@ -2112,6 +2131,12 @@ export type InlineQueryContext<C extends Context> = Filter<
C,
"inline_query"
>;

type ChatTypeContextCore<T extends Chat["type"]> =
& Record<"update", ChatTypeUpdate<T>> // ctx.update
& ChatType<T> // ctx.chat
& ChatTypeRecord<"msg", T> // ctx.msg
& AliasProps<ChatTypeUpdate<T>>; // ctx.message etc
/**
* Type of the context object that is available inside the handlers for
* `bot.chatType`.
Expand All @@ -2124,10 +2149,7 @@ export type InlineQueryContext<C extends Context> = Filter<
*/
export type ChatTypeContext<C extends Context, T extends Chat["type"]> =
& C
& Record<"update", ChatTypeUpdate<T>> // ctx.update
& ChatType<T> // ctx.chat
& ChatTypeRecord<"msg", T> // ctx.msg
& AliasProps<ChatTypeUpdate<T>>; // ctx.message etc
& ChatTypeContextCore<T>;
type ChatTypeUpdate<T extends Chat["type"]> =
& ChatTypeRecord<
| "message"
Expand Down
17 changes: 14 additions & 3 deletions src/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,10 @@ type Combine<U, K extends string> = U extends unknown
? U & Partial<Record<Exclude<K, keyof U>, undefined>>
: never;

export type FilterCore<Q extends FilterQuery> = PerformQueryCore<
RunQuery<ExpandShortcuts<Q>>
>;

/**
* This type infers which properties will be present on the given context object
* provided it matches the given filter query. If the filter query is a union
Expand All @@ -446,17 +450,24 @@ export type Filter<C extends Context, Q extends FilterQuery> = PerformQuery<
C,
RunQuery<ExpandShortcuts<Q>>
>;
type PerformQueryCore<U extends SomeObject> = U extends unknown
? FilteredContextCore<Update & U>
: never;
// apply a query result by intersecting it with Update, and then injecting into C
type PerformQuery<C extends Context, U extends SomeObject> = U extends unknown
? FilteredContext<C, Update & U>
: never;
// set the given update into a given context object, and adjust the aliases
type FilteredContext<C extends Context, U extends Update> =
& C

type FilteredContextCore<U extends Update> =
& Record<"update", U>
& AliasProps<Omit<U, "update_id">>
& Shortcuts<U>;

// set the given update into a given context object, and adjust the aliases
type FilteredContext<C extends Context, U extends Update> =
& C
& FilteredContextCore<U>;

// helper type to infer shortcuts on context object based on present properties, must be in sync with shortcut impl!
interface Shortcuts<U extends Update> {
msg: [U["callback_query"]] extends [SomeObject]
Expand Down
30 changes: 30 additions & 0 deletions test/composer.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Composer, Context } from "../src/mod.ts";

function _f<C extends Context>() {
const c = new Composer<C & { state: 1 }>();
c.use((ctx) => {
if (ctx.has(":contact")) {
ctx.msg.contact.phone_number;
ctx.state;
}
if (ctx.hasText("123")) {
ctx.match.includes;
}
if (ctx.hasCommand("123")) {
ctx.match.charCodeAt;
}
if (ctx.hasChatType("private")) {
ctx.chat.type;
}
if (ctx.hasGameQuery("123")) {
ctx.callbackQuery.game_short_name;
}
if (ctx.hasInlineQuery("123")) {
ctx.inlineQuery.id;
}
ctx.state;
});
c.command("c", (ctx) => {
ctx.match.charCodeAt;
});
}

0 comments on commit e9ae05b

Please sign in to comment.