Skip to content

"Expression produces a union type that is too complex to represent" when using interfaces with large number of params for filtering and narrowing down certain keysΒ #57863

Closed
@SecondThundeR

Description

@SecondThundeR

πŸ”Ž 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

https://www.typescriptlang.org/play?ts=5.4.2#code/KYDwDg9gTgLgBASwHY2FAZgQwMbDgVTABNNU4BvAKDhrgFdjTgB9BIgLjiToFsAjNAG5K1Wj2ABnCZgDmwAPydyccVNnBOEmFGQy4AX2G04wIglRFmq6XMUUTZi1ck2NcLTqR7DomtgAWmEhIwAA2zJBadsoBQSHhkTCa2roGRrSm5qbMscFhERBRSg5ZlrnxBVrJnt7pNNbqzFDAODAIEEjRKi6Nza3tSNWpPsYNck0t2G0dORB0KF1jLH1TA7PzSe4pXmm+iEihyCwAjnRoAJ5dyIchzKcXQzsjtAGFwEisB0cTEnShMF1XhJ3p8bstJH9Nh5hnU4NhMKFQnwcABrO5nKCXYrwxHI7Bo+6Yx61PYSfwIMBgXToi5dMkUqleGlErY1XbGMDNHL+YD4uYwZlY+yclgBXko-mC4ns2iQRFdOWhaXPGiK5hBCQAdzQCogiPVSC1aGVsJ4525pGc-B1xTNFoF4mtUBNe1iDuATsBgXdTpdxjdzAAVhBkBN7kV7AHg6HmuGodsSf7vcw+BBCgDscnU+m-bRmjwIAA3bIB7MR5T5oslrNpqqsmGUfQiGDnMB4ABiCH+aHb81WHQAPPgTCBUEgiBICIxUAAaOAANRHY4nBAAfHAALxwAAUDE4+AAlJv1wxEJP58JKKBILA4Og+9MkCpSAFO92oEOl+8V4QSLO4AAil+46Tm+qBQABGLnKu257OgXbgZwQEAD6AQA2gAujOlAHpwYE9g+AxDnO+Efvgc4Aau65UP6HQSHqwAAHShBAMjbgARAAsi+5JeOxc7we+B6ws0MB0FAT67mA+64fQYBnnApHEYB64buuACE6kMMITaUAA9AAVAZcAAIKInAhYImwd4IWgcCEggkg7sAxaYnJf54CiwDnCOuBgPADAeawRBHgZelXuA0DwC2baKbZEFQZucBeecEDoHAADyPDmEO07AHO7GBUwwXsaul6GcZABymBQFAECanARD1U+QTuUwcAQHwga8vAyLAkQHUtTZ772YlYURTe0Wth28WfqAy6Tr+TAUcBK6kZBFyqXAAAKaDoNAPAbZiA57ORewAErzEd5wDpRlBlSIel6XAcghFA7WtZ13VTKydBTOJeAwN6cKtQI+zgcCUymHAmrmP4bWoJOMAQFwNV1Q1QMeo1zWUDFeCXUg123atk7Ql4W0AMIQPwRwDgAMgAjO270yOIKC3auFEPfpz08JgYCTpM8NgDV8BpXAmPDeBo0XBLKPnby0BEAO7EpfxHVdT1q649NcCM8zshszAxPzd+pMJltQGmyB9BICiSDNXA8hwAr2BK7dc5fVreycCErnCE9jXAPBIRwAioQdZjUA2WEK7oHVPAy25mCTilk6w0DFkImccAAAbzEQwdHEQuc67FVM0yEykANIk-W5NJcO1srvM9s48YzvDgAZDtosIAiA6u+7ACiIDYKEdCFwO1dzil4v4JztuFyHphUT7XAuUIj3PfzYChD5mBJz5zS-P8cB8D5yAQz1qTmDDcMI-lPNh+OEs8k+dASKkmPAlnE9OcjDqrkdCFzfngVodAESPzLngXaGADpEybqOM2U4PJznOnXL2UwtoYObpOVuDtNRID2M7Uiphh7FnZuRVB7Ue7nW1sYX2m8oDlWekgNG9VsZEJfo-c+l8UBoEhm0HYd8M7w0PmYdA6A0DvACnlGBcV3zkMocbYedclr-iQQtGhqBVJ7B7sPfRmVsrG2oYVPKJVuZ4zgJxNgRBQjAE1DVYAc1kE2w0cALa24pggBkseW2bduGoW2gnBAwIBwEOag9ceKdJwUNkQACToHwVx2iPHUT2J-YA25GK5OykQexjjnGcBMrVTAN1bEFIcU45oQ4qKyXiSgJJKSF4UD2LRQ0DFmKsQ4iZApuh1b5MKTUpiDivBAwKq9NACBsBwECOOBxUAJDsWEu0vMwAxISTfmE2ETZjCDitm4ta8VrowTWTQQSiFAJwFQgBTC2FjDGFyYxIZ1TimmTKRUuxbzalKWoZRNexgGkqOaQOP5XMMmPJeHRLpLE2LsT6WYPic5XlFOaMxd4MgJlwHYvMwpSy7zQHVpctAqyoU0FEuJJ8IQGqNJgKC8FKkYJktoE2XSbtDTwH8MkpKNK4B0uaduVZlBuV8EYh0DiSwVnCFFeKpAkqehyHVlJI8akKD6FWRyrQ7geVbllRKtC7EpUFXKPkRI7EMKrN+GKrJ24VX+PIBqmVyS5XbkNcanFpqEjpgtXOIVDqnWUCAA

πŸ’» 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

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issueRecent RegressionThis is a new regression just found in the last major/minor version of TypeScript.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions