Closed
Description
π Search Terms
Expression produces a union type that is too complex to represent, 2590, ts2590
π Version & Regression Information
- This behavior occurs in 5.4.2 and above. On 5.3.3 it works fine
β― Playground Link
π» Code
export interface Update {
update_id: number;
message?: { message: string };
edited_message?: { edited_message: string };
channel_post?: { channel_post: string };
edited_channel_post?: { edited_channel_post: string };
message_reaction?: { message_reaction: string };
message_reaction_count?: { message_reaction_count: string };
inline_query?: { inline_query: string };
chosen_inline_result?: { chosen_inline_result: string };
callback_query?: { callback_query: string };
shipping_query?: { shipping_query: string };
pre_checkout_query?: { pre_checkout_query: string };
poll?: { poll: string };
poll_answer?: { poll_answer: string };
my_chat_member?: { my_chat_member: string };
chat_member?: { chat_member: string };
chat_join_request?: { chat_join_request: string };
chat_boost?: { chat_boost: string };
removed_chat_boost?: { removed_chat_boost: string };
}
type FilterFunction<U extends Update, V extends U> = (up: U) => up is V;
export function matchFilter<U extends Update, Q extends FilterQuery>(
filter: Q | Q[],
): FilterFunction<U, Filter<U, Q>> {
// ^ errors out
console.log("Matching", filter);
return (up: U): up is Filter<U, Q> => !!up;
}
/** All valid filter queries (every update key except update_id) */
export type FilterQuery = keyof Omit<Update, "update_id">;
/** Narrow down an update object based on a filter query */
export type Filter<U extends Update, Q extends FilterQuery> = PerformQuery<
U,
RunQuery<Q>
>;
// generate an object structure that can be intersected with updates to narrow them down
type RunQuery<Q extends string> = Combine<L1Fragment<Q>, Q>;
// maps each part of the filter query to Record<"key", object>
type L1Fragment<Q extends string> = Q extends unknown ? Record<Q, object>
: never;
// define all other fields from query as keys with value `undefined`
type Combine<U, K extends string> = U extends unknown
? U & Partial<Record<Exclude<K, keyof U>, undefined>>
: never;
// apply a query result by intersecting it with update,
// and then using these values to override the actual update
type PerformQuery<U extends Update, R extends object> = R extends unknown
? FilteredEvent<U, Update & R>
: never;
// narrow down an update by intersecting it with a different update
type FilteredEvent<E extends Update, U extends Update> =
& E
& Omit<U, "update_id">;
type Middleware<U extends Update> = (ctx: U) => unknown | Promise<unknown>;
class EventHub<U extends Update> {
use(...middleware: Array<Middleware<U>>): EventHub<U> {
console.log("Adding", middleware.length, "generic handlers");
return this;
}
on<Q extends FilterQuery>(
filter: Q | Q[],
...middleware: Array<Middleware<Filter<U, Q>>>
// ^ errors out
): EventHub<Filter<U, Q>> {
console.log("Adding", middleware.length, "handlers for", filter);
return new EventHub<Filter<U, Q>>();
}
}
π Actual behavior
TypeScript compiler throws "Expression produces a union type that is too complex to represent" on Filter
type instantiation with large interface
π Expected behavior
TypeScript compiler shouldn't throw "Expression produces a union type that is too complex to represent" on Filter
type instantiation with large interface
Additional information about the issue
On 5.3.3, such issue doesn't occur (even when we are adding more parameters to Update
interface, e.g. additional 20-30 parameters)
But, on 5.4.2 it seems like limit for it is lowered down. When chat_boost
and removed_chat_boost
are removed, both Filter
instantiations no longer throwing errors