I'm trying to use Telegraf (4.6.0) types and I'm having an issue with exploring possible message properties.
Here's what I do now:
import { Telegraf, Context } from 'telegraf'
const myBot = new Telegraf(myToken)
listenerBot.on('message', (ctx: Context) => {
const {
text,
forward_from_chat, forward_from_message_id,
photo, caption, caption_entities,
// what it will contain if there's a video? audio? document? ...
} = ctx.message as any
// do stuff with message
}
Since messages can be of various types (in both non-TS and TS sense), when I type ctx.message.
in IDE (VS Code in my case) I'm only suggested the props that are always in message object (like message_id
). Yes, I can do things like
if('text' in ctx.message) {
// do stuff with ctx.message.text
}
but this doesn't help me explore what props can ctx.message
hold. I can imagine a hacky way like
class ExploredContext = ExploreProps<Context> → gives a class similar to Context,
but all possible props are non-optional
...
(ctx as ExploredContext).message._ // cursor here, IDE shows possilbe props
(ctx.message as ExploredMessage)._ // or like this
but neither I know how to implement things like ExploreProps
helper (I'm only aware of utility types) nor I know any better, non-hacky ways to get this (like some configuration of typescript and/or IDE).
Can you suggest a way to implement ExploreProps
or a better way to explore possible props?
(in Telegraf context, I've also asked at in an issue, but a consistent solution would be helpful without regard to Telegraf itself)
CodePudding user response:
You can flatten a union using StrictUnion
as defined here This type will basically add the missing members to all union constituents with the type undefined
. This will allow de-structuring to suggest all members from any constituent, but each member that is not present in all union constituents will also contain undefined
(which is probably for the best from a type-safety perspective)
import { Telegraf, Context } from 'telegraf'
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, undefined>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
const myToken = ""
const myBot = new Telegraf(myToken)
myBot.on('message', (ctx: Context) => {
const {
text,
forward_from_chat, forward_from_message_id,
photo, caption, caption_entities,
} = ctx.message as StrictUnion<Context['message']>
})