Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TS hangs up entire computer, type of variables stuck at "Loading..." #58373

Open
slyofzero opened this issue Apr 30, 2024 · 16 comments
Open

TS hangs up entire computer, type of variables stuck at "Loading..." #58373

slyofzero opened this issue Apr 30, 2024 · 16 comments
Assignees
Labels
Bug A bug in TypeScript

Comments

@slyofzero
Copy link

slyofzero commented Apr 30, 2024

🔎 Search Terms

TS intellisense not working, TS hangs up entire PC, Type inference not working

🕗 Version & Regression Information

  • This is a crash using version 5.4.5
  • I tried versions 5.4.3, 5.4.5, and 5.5
  • This issue sprung up out of nowhere, I have no idea about the origin

⏯ Reproduction steps

To reproduce this issue please clone the following repository - https://github.com/slyofzero/TONBuyBot
Install the packages using pnpm - pnpm i
Visit the file - ./src/bot/commands/index.ts

💻 Code

The file code in question is very tame -

import { teleBot } from "@/index";
import { startBot } from "./start";
import { log, errorHandler } from "@/utils/handlers";
import { settings } from "./settings";
import { setEmojiCommand } from "./setEmoji";
import { stopBot } from "./stop";
import { setGifCommand } from "./setGif";

export function initiateBotCommands() {
  teleBot.api
    .setMyCommands([
      { command: "start", description: "Start the bot" },
      { command: "stop", description: "Stop the bot" },
      { command: "settings", description: "To customize the bot" },
      { command: "setemoji", description: "To set an emoji" },
      { command: "setgif", description: "To set a GIF" },
    ])
    .catch((e) => errorHandler(e));

  teleBot.command("start", (ctx) => startBot(ctx));
  teleBot.command("stop", (ctx) => stopBot(ctx));
  teleBot.command("settings", (ctx) => settings(ctx));
  teleBot.command("setemoji", (ctx) => setEmojiCommand(ctx));

  teleBot.hears(/\/setgif/, (ctx) => setGifCommand(ctx, true));
  // @ts-expect-error CTX type invalid
  teleBot.on(":animation", (ctx) => setGifCommand(ctx));

  log("Bot commands up");
}

The above code is used to setup a telegram bot's commands using grammy.js

🙁 Actual behavior

Update - The issue only occurs when I open the ./src/bot/commands/index.ts file of the repository I share above in reproduction steps.

Uptil yesterday night VSCode and Typescript Intellisense worked fine but now types of variables upon hovering are indefinitely stuck at "Loading..."

image

This is followed by a high spike in CPU and RAM usage

image

The icon to the bottom right showing TS server status keeps spinning followed followed by the spike in CPU and RAM usage mentioned above.

image

Because of all these issues, using the computer with the project on is next to impossible. Typescript syntax errors, Intellisense, or Type inference, nothing works. I first tried uninstalling and reinstalling VSCode but that didn't fix it. I formatted by hard drive and reinstalled my OS but the problem still persists. I have gone through the below two issues that sounds similar to my problem but haven't found a fix yet.

#35512
microsoft/vscode#87870

🙂 Expected behavior

The expected behaviour would be smooth Intellisense, type inference, and an average usage of CPU and RAM not affecting other functionalities of my PC.

Additional information about the issue

I have tried this issue on two different devices and two different OS and been able to reproduce on both.

First device was an i3, 8GB RAM Dell Inspiron 5482 with Windows 10
Second device is an AMD Ryzen 7 6800, 32GB RAM, 8GB RTX 3070, Lenovo Legion 5 Pro with Windows 11

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label May 2, 2024
@RyanCavanaugh
Copy link
Member

We need a way to reproduce this

@slyofzero
Copy link
Author

We need a way to reproduce this

I have updated the original issue with reproduction steps and additional information about how I have tested it

@RyanCavanaugh RyanCavanaugh removed the Needs More Info The issue still hasn't been fully clarified label May 2, 2024
@RyanCavanaugh
Copy link
Member

tsc is also not terminating in a reasonable amount of time. Bisecting...

@RyanCavanaugh
Copy link
Member

Bisects to #57345

@RyanCavanaugh
Copy link
Member

Using just grammy as a dependency:

import { Bot } from "grammy";
export const teleBot = new Bot("");
teleBot.on(":animation", c => c);

@RyanCavanaugh
Copy link
Member

Before

Identifiers:                57697
Symbols:                    37871
Types:                       9556
Instantiations:            240994
Memory used:               84999K
Assignability cache size:   18439
Identity cache size:          128
Subtype cache size:             0
Strict subtype cache size:      0
I/O Read time:              0.01s
Parse time:                 0.19s
ResolveModule time:         0.02s
ResolveTypeReference time:  0.00s
ResolveLibrary time:        0.01s
Program time:               0.25s
Bind time:                  0.07s
Check time:                 0.34s

After

Identifiers:                  53327
Symbols:                      38110
Types:                       535156
Instantiations:              243474
Memory used:               2670146K
Assignability cache size:     35533
Identity cache size:            116
Subtype cache size:               0
Strict subtype cache size:       42
I/O Read time:                0.01s
Parse time:                   0.14s
ResolveModule time:           0.02s
ResolveTypeReference time:    0.01s
ResolveLibrary time:          0.01s
Program time:                 0.21s
Bind time:                    0.07s
Check time:                  30.54s

@RyanCavanaugh
Copy link
Member

Basically stuck here:

import { Filter, FilterQuery } from "grammy";
import { Context } from "grammy/out/context.js";
// Uncomment constraint to repro
type MiddlewareFn<C /*extends Context*/> = (ctx: C) => void;
declare function on<Q extends FilterQuery>(middleware: MiddlewareFn<Filter<Context, Q>>): void;

@Andarist
Copy link
Contributor

Andarist commented May 2, 2024

and there go my weekend plans... 😭

@RyanCavanaugh
Copy link
Member

@ahejlsberg assigning to you since #57343 also was, but sounds like @Andarist might be working on it?

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label May 2, 2024
@slyofzero
Copy link
Author

Bisects to #57345

what seems to be the issue here? grammy.js is at fault?

@fatcerberus
Copy link

Bisects to #57345

what seems to be the issue here? grammy.js is at fault?

No, TS is. It's a regression caused by a change that was made in the linked PR (#57345), which was found via git bisect. grammy just happens to be the module that triggers the bug.

@ahejlsberg
Copy link
Member

ahejlsberg commented May 3, 2024

@RyanCavanaugh I'm unable to reproduce with the examples here and here. The second example (after uncommenting the constraint) shows this:

Files:              53
Lines:           48693
Identifiers:     51490
Symbols:         34834
Types:            3750
Instantiations:  87521
Memory used:    70217K
I/O read:        0.01s
I/O write:       0.00s
Parse time:      0.23s
Bind time:       0.07s
Check time:      0.14s
Emit time:       0.01s
Total time:      0.45s

This is with the compiler in main branch, following tsc -init and npm i grammy. Am I missing something?

@RyanCavanaugh
Copy link
Member

@ahejlsberg Try this? https://github.com/RyanCavanaugh/repro-58373

Relevant numbers on main

Files:                           59
Lines of Library:             36690
Lines of Definitions:         13738
Identifiers:                  53544
Symbols:                      36350
Types:                       532823
Instantiations:              235429
Memory used:               2693736K
Assignability cache size:     35030
Check time:                  27.66s
Total time:                  27.95s

@ahejlsberg
Copy link
Member

Ok, I can reproduce with your repo. The difference is you're using an older version of grammy, 1.20.3. Current version on NPM is 1.22.4 and it doesn't have a problem.

@ahejlsberg
Copy link
Member

See grammyjs/grammY#553. Looks like this is a known issue that was fixed by in the latest grammy. I'd still be interested in knowing why #57345 slowed down the old types, but appears there's a simple workaround.

@Andarist
Copy link
Contributor

Andarist commented Jun 3, 2024

I'm still working on minimizing this further but I want to share a single-file repro created based on the code from grammy. Some parts of this probably no longer make sense (I remove bits that make sense for what their types want to achieve but which are not relevant for the repro):

repro
type NotUndefined = string | number | boolean | object;
type SnakeToCamelCase<S extends string> = S extends `${infer L}_${infer R}`
  ? `${L}${R}`
  : S;
type AliasProps<U> = {
  [K in string & keyof U as SnakeToCamelCase<K>]: U[K];
};
interface Update {
  update_id: number;
}
type RenamedUpdate = AliasProps<Update>;
declare class Context implements RenamedUpdate {
  updateid: number;
}

declare const UPDATE_KEYS: {
  readonly message: {
    readonly new_chat_members: {
      readonly me: {};
      readonly is_bot: {};
      readonly is_premium: {};
      readonly added_to_attachment_menu: {};
    };
    readonly left_chat_member: {
      readonly me: {};
      readonly is_bot: {};
      readonly is_premium: {};
      readonly added_to_attachment_menu: {};
    };
    readonly group_chat_created: {};
    readonly supergroup_chat_created: {};
    readonly migrate_to_chat_id: {};
    readonly migrate_from_chat_id: {};
    readonly successful_payment: {};
    readonly users_shared: {};
    readonly chat_shared: {};
    readonly connected_website: {};
    readonly write_access_allowed: {};
    readonly passport_data: {};
    readonly forum_topic_created: {};
    readonly forum_topic_edited: {
      readonly name: {};
      readonly icon_custom_emoji_id: {};
    };
    readonly forum_topic_closed: {};
    readonly forum_topic_reopened: {};
    readonly general_forum_topic_hidden: {};
    readonly general_forum_topic_unhidden: {};
    readonly forward_origin: {
      readonly user: {};
      readonly hidden_user: {};
      readonly chat: {};
      readonly channel: {};
    };
    readonly is_topic_message: {};
    readonly is_automatic_forward: {};
    readonly text: {};
    readonly animation: {};
    readonly audio: {};
    readonly document: {};
    readonly photo: {};
    readonly sticker: {
      readonly is_video: {};
      readonly is_animated: {};
      readonly premium_animation: {};
    };
    readonly story: {};
    readonly video: {};
    readonly video_note: {};
    readonly voice: {};
    readonly contact: {};
    readonly dice: {};
    readonly game: {};
    readonly poll: {};
    readonly venue: {};
    readonly location: {};
    readonly entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption_entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption: {};
    readonly has_media_spoiler: {};
    readonly new_chat_title: {};
    readonly new_chat_photo: {};
    readonly delete_chat_photo: {};
    readonly message_auto_delete_timer_changed: {};
    readonly pinned_message: {};
    readonly invoice: {};
    readonly proximity_alert_triggered: {};
    readonly video_chat_scheduled: {};
    readonly video_chat_started: {};
    readonly video_chat_ended: {};
    readonly video_chat_participants_invited: {};
    readonly web_app_data: {};
  };
  readonly edited_message: {
    readonly new_chat_members: {
      readonly me: {};
      readonly is_bot: {};
      readonly is_premium: {};
      readonly added_to_attachment_menu: {};
    };
    readonly left_chat_member: {
      readonly me: {};
      readonly is_bot: {};
      readonly is_premium: {};
      readonly added_to_attachment_menu: {};
    };
    readonly group_chat_created: {};
    readonly supergroup_chat_created: {};
    readonly migrate_to_chat_id: {};
    readonly migrate_from_chat_id: {};
    readonly successful_payment: {};
    readonly users_shared: {};
    readonly chat_shared: {};
    readonly connected_website: {};
    readonly write_access_allowed: {};
    readonly passport_data: {};
    readonly forum_topic_created: {};
    readonly forum_topic_edited: {
      readonly name: {};
      readonly icon_custom_emoji_id: {};
    };
    readonly forum_topic_closed: {};
    readonly forum_topic_reopened: {};
    readonly general_forum_topic_hidden: {};
    readonly general_forum_topic_unhidden: {};
    readonly forward_origin: {
      readonly user: {};
      readonly hidden_user: {};
      readonly chat: {};
      readonly channel: {};
    };
    readonly is_topic_message: {};
    readonly is_automatic_forward: {};
    readonly text: {};
    readonly animation: {};
    readonly audio: {};
    readonly document: {};
    readonly photo: {};
    readonly sticker: {
      readonly is_video: {};
      readonly is_animated: {};
      readonly premium_animation: {};
    };
    readonly story: {};
    readonly video: {};
    readonly video_note: {};
    readonly voice: {};
    readonly contact: {};
    readonly dice: {};
    readonly game: {};
    readonly poll: {};
    readonly venue: {};
    readonly location: {};
    readonly entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption_entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption: {};
    readonly has_media_spoiler: {};
    readonly new_chat_title: {};
    readonly new_chat_photo: {};
    readonly delete_chat_photo: {};
    readonly message_auto_delete_timer_changed: {};
    readonly pinned_message: {};
    readonly invoice: {};
    readonly proximity_alert_triggered: {};
    readonly video_chat_scheduled: {};
    readonly video_chat_started: {};
    readonly video_chat_ended: {};
    readonly video_chat_participants_invited: {};
    readonly web_app_data: {};
  };
  readonly channel_post: {
    readonly channel_chat_created: {};
    readonly forward_origin: {
      readonly user: {};
      readonly hidden_user: {};
      readonly chat: {};
      readonly channel: {};
    };
    readonly is_topic_message: {};
    readonly is_automatic_forward: {};
    readonly text: {};
    readonly animation: {};
    readonly audio: {};
    readonly document: {};
    readonly photo: {};
    readonly sticker: {
      readonly is_video: {};
      readonly is_animated: {};
      readonly premium_animation: {};
    };
    readonly story: {};
    readonly video: {};
    readonly video_note: {};
    readonly voice: {};
    readonly contact: {};
    readonly dice: {};
    readonly game: {};
    readonly poll: {};
    readonly venue: {};
    readonly location: {};
    readonly entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption_entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption: {};
    readonly has_media_spoiler: {};
    readonly new_chat_title: {};
    readonly new_chat_photo: {};
    readonly delete_chat_photo: {};
    readonly message_auto_delete_timer_changed: {};
    readonly pinned_message: {};
    readonly invoice: {};
    readonly proximity_alert_triggered: {};
    readonly video_chat_scheduled: {};
    readonly video_chat_started: {};
    readonly video_chat_ended: {};
    readonly video_chat_participants_invited: {};
    readonly web_app_data: {};
  };
  readonly edited_channel_post: {
    readonly channel_chat_created: {};
    readonly forward_origin: {
      readonly user: {};
      readonly hidden_user: {};
      readonly chat: {};
      readonly channel: {};
    };
    readonly is_topic_message: {};
    readonly is_automatic_forward: {};
    readonly text: {};
    readonly animation: {};
    readonly audio: {};
    readonly document: {};
    readonly photo: {};
    readonly sticker: {
      readonly is_video: {};
      readonly is_animated: {};
      readonly premium_animation: {};
    };
    readonly story: {};
    readonly video: {};
    readonly video_note: {};
    readonly voice: {};
    readonly contact: {};
    readonly dice: {};
    readonly game: {};
    readonly poll: {};
    readonly venue: {};
    readonly location: {};
    readonly entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption_entities: {
      readonly mention: {};
      readonly hashtag: {};
      readonly cashtag: {};
      readonly bot_command: {};
      readonly url: {};
      readonly email: {};
      readonly phone_number: {};
      readonly bold: {};
      readonly italic: {};
      readonly underline: {};
      readonly strikethrough: {};
      readonly spoiler: {};
      readonly code: {};
      readonly pre: {};
      readonly text_link: {};
      readonly text_mention: {};
      readonly custom_emoji: {};
    };
    readonly caption: {};
    readonly has_media_spoiler: {};
    readonly new_chat_title: {};
    readonly new_chat_photo: {};
    readonly delete_chat_photo: {};
    readonly message_auto_delete_timer_changed: {};
    readonly pinned_message: {};
    readonly invoice: {};
    readonly proximity_alert_triggered: {};
    readonly video_chat_scheduled: {};
    readonly video_chat_started: {};
    readonly video_chat_ended: {};
    readonly video_chat_participants_invited: {};
    readonly web_app_data: {};
  };
  readonly inline_query: {};
  readonly chosen_inline_result: {};
  readonly callback_query: {
    readonly data: {};
    readonly game_short_name: {};
  };
  readonly shipping_query: {};
  readonly pre_checkout_query: {};
  readonly poll: {};
  readonly poll_answer: {};
  readonly my_chat_member: {
    readonly from: {
      readonly me: {};
      readonly is_bot: {};
      readonly is_premium: {};
      readonly added_to_attachment_menu: {};
    };
  };
  readonly chat_member: {
    readonly from: {
      readonly me: {};
      readonly is_bot: {};
      readonly is_premium: {};
      readonly added_to_attachment_menu: {};
    };
  };
  readonly chat_join_request: {};
  readonly message_reaction: {
    readonly old_reaction: {
      readonly emoji: {};
      readonly custom_emoji: {};
    };
    readonly new_reaction: {
      readonly emoji: {};
      readonly custom_emoji: {};
    };
  };
  readonly message_reaction_count: {
    readonly reactions: {
      readonly emoji: {};
      readonly custom_emoji: {};
    };
  };
};

type KeyOf<T> = string & keyof T;
type S = typeof UPDATE_KEYS;
type L1S = KeyOf<S>;

type L2S<L1 extends L1S = L1S> = L1 extends unknown
  ? `${L1}:${KeyOf<S[L1]>}`
  : never;

type L3S<L1 extends L1S = L1S> = L1 extends unknown ? L3S_<L1> : never;

type L3S_<
  L1 extends L1S,
  L2 extends KeyOf<S[L1]> = KeyOf<S[L1]>,
> = L2 extends unknown ? `${L1}:${L2}:${KeyOf<S[L1][L2]>}` : never;

type L123 = L1S | L2S | L3S;

type CollapseL1<
  Q extends string,
  L extends L1Shortcuts = Exclude<L1Shortcuts, "">,
> =
  | Q
  | (L extends string
      ? Q extends (typeof L1_SHORTCUTS)[L][number]
        ? L
        : never
      : never);

type CollapseL2<
  Q extends string,
  L extends L2Shortcuts = Exclude<L2Shortcuts, "">,
> =
  | Q
  | (L extends string
      ? Q extends (typeof L2_SHORTCUTS)[L][number]
        ? L
        : never
      : never);

declare const L1_SHORTCUTS: {
  readonly "": readonly ["message", "channel_post"];
  readonly msg: readonly ["message", "channel_post"];
  readonly edit: readonly ["edited_message", "edited_channel_post"];
};
declare const L2_SHORTCUTS: {
  readonly "": readonly ["entities", "caption_entities"];
  readonly media: readonly ["photo", "video"];
  readonly file: readonly [
    "photo",
    "animation",
    "audio",
    "document",
    "video",
    "video_note",
    "voice",
    "sticker",
  ];
};
type L1Shortcuts = KeyOf<typeof L1_SHORTCUTS>;
type L2Shortcuts = KeyOf<typeof L2_SHORTCUTS>;

type InjectShortcuts<Q extends L123 = L123> =
  Q extends `${infer L1}:${infer L2}:${infer L3}`
    ? `${CollapseL1<L1, L1Shortcuts>}:${CollapseL2<L2, L2Shortcuts>}:${L3}`
    : Q extends `${infer L1}:${infer L2}`
    ? `${CollapseL1<L1, L1Shortcuts>}:${CollapseL2<L2>}`
    : CollapseL1<Q>;
type FilterQuery = InjectShortcuts;

type ExpandL1<S extends string> = S extends L1Shortcuts
  ? (typeof L1_SHORTCUTS)[S][number]
  : S;

type ExpandL2<S extends string> = S extends L2Shortcuts
  ? (typeof L2_SHORTCUTS)[S][number]
  : S;

type ExpandShortcuts<Q extends string> =
  Q extends `${infer L1}:${infer L2}:${infer L3}`
    ? `${ExpandL1<L1>}:${ExpandL2<L2>}:${L3}`
    : Q extends `${infer L1}:${infer L2}`
    ? `${ExpandL1<L1>}:${ExpandL2<L2>}`
    : ExpandL1<Q>;

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

type FilteredContext<C extends Context, U extends Update> = C &
  FilteredContextCore<U>;

type PerformQuery<C extends Context, U extends object> = U extends unknown
  ? FilteredContext<C, Update & U>
  : never;

type Filter<C extends Context, Q extends FilterQuery> = PerformQuery<
  C,
  RunQuery<ExpandShortcuts<Q>>
>;

type L1Equivalents = {
  message: "from";
  edited_message: "from" | "edit_date";
  channel_post: "sender_chat";
  edited_channel_post: "sender_chat" | "edit_date";
};

type TwinsFromL1<L1 extends string, L2 extends string> =
  L1 extends KeyOf<L1Equivalents> ? L1Equivalents[L1] : L2;

type L2Equivalents = {}

type TwinsFromL2<L1 extends string, L2 extends string> =
  L1 extends KeyOf<L2Equivalents>
    ? L2 extends KeyOf<L2Equivalents[L1]>
      ? L2Equivalents[L1][L2]
      : L2
    : L2;

type AddTwins<L1 extends string, L2 extends string> =
  | TwinsFromL1<L1, L2>
  | TwinsFromL2<L1, L2>

type L2ShallowFragment<L1 extends string> = Record<
  AddTwins<L1, never>,
  NotUndefined
>;

type L2Fragment<L1 extends string, L2 extends string> = L2 extends unknown
  ? Record<L2 | AddTwins<L1, L2>, NotUndefined>
  : never;

type L2Discriminator<L1 extends string, L2 extends string> = [L2] extends [
  never,
]
  ? L2ShallowFragment<L1>
  : Combine<L2Fragment<L1, L2>, L2>;

type L1Parts<Q extends string> = Q extends `${infer L1}:${string}` ? L1 : Q;

type L2Parts<
  Q extends string,
  L1 extends string,
> = Q extends `${L1}:${infer L2}:${string}`
  ? L2
  : Q extends `${L1}:${infer L2}`
    ? L2
    : never;

type L1Fragment<Q extends string, L1 extends string> = L1 extends unknown
  ? Record<L1, L2Discriminator<L1, L2Parts<Q, L1>>>
  : never;

type L1Discriminator<Q extends string, L1 extends string> = Combine<
  L1Fragment<Q, L1>,
  L1
>;

type Combine<U, K extends string> = U extends unknown
  ? U & Partial<Record<Exclude<K, keyof U>, undefined>>
  : never;

type RunQuery<Q extends string> = L1Discriminator<Q, L1Parts<Q>>;

type MiddlewareFn<C extends Context> = (ctx: C) => void;
declare function on<Q extends FilterQuery>(
  middleware: MiddlewareFn<Filter<Context, Q>>,
): void;

I will continue working on this - so far, what I was able to notice is that AliasProps being a renaming mapped type impacts this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants