grammyjs / grammY

The Telegram Bot Framework.
https://grammy.dev
MIT License
2.37k stars 117 forks source link

Improve inference when using ctx.hasChatType with multiple chat types #619

Open rayz1065 opened 3 months ago

rayz1065 commented 3 months ago

Suppose we have a set of chat types we want to listen for within a middleware, and a handler we need to call on match:

const chatTypes = [
  'channel',
  'group',
  'supergroup',
] as const satisfies Chat['type'][];

const handler: <C extends Context>(
  ctx: ChatTypeContext<C, (typeof chatTypes)[number]>
) => Promise<unknown> = async (ctx) => {
  // do something
};

I expect both of the following pieces of code to work properly, but while the first one works fine, the second one shows an error:

const composer = new Composer<Context>();

composer.chatType(chatTypes).use(async (ctx, next) => {
  await handler(ctx); // ok
  return next();
});

composer.use(async (ctx, next) => {
  if (ctx.hasChatType(chatTypes)) {
    await handler(ctx); // error
  }
  return next();
});

As mentioned, the error only occurs when more than one chat is used for filtering.

Argument of type 'Context & Record<"update", ChatTypeUpdate<"channel" | "group" | "supergroup">> & ChatType<"channel" | "group" | "supergroup"> & Record<...> & ChatFrom<...> & Partial<...> & AliasProps<...>' is not assignable to parameter of type 'ChatTypeContext<Context & Record<"update", ChatTypeUpdate<"channel" | "group" | "supergroup">> & ChatType<"channel" | "group" | "supergroup"> & ChatFrom<...> & Partial<...> & AliasProps<...>, "channel" | ... 1 more ... | "supergroup">'.
// ...
      Types of property 'update' are incompatible.
// ...
                    Types of property 'chat' are incompatible.
                      Type '(GroupChat & { type: "channel" | "group" | "supergroup"; }) | (SupergroupChat & { type: "channel" | "group" | "supergroup"; })' is not assignable to type '{ type: "supergroup"; }'.
                        Type 'GroupChat & { type: "channel" | "group" | "supergroup"; }' is not assignable to type '{ type: "supergroup"; }'.
                          Types of property 'type' are incompatible.
                            Type '"group"' is not assignable to type '"supergroup"'.ts(2345)

It could be useful to investigate why the two pieces of code are behaving in a different way, as the second version has some use-cases not entirely covered by the first one.

KnorpelSenf commented 3 months ago

Note to self: most likely related to distributed union types

KnorpelSenf commented 1 week ago

This also seems to happen with filter queries:

const media = await conversation.waitFor(":media"); // photo or video
if (media.has(":photo")) {
  const resolutions = media.msg.photo.length;
} else  {
  const { width, height } = media.msg.video; // error!
}