mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Divide-and-conquer strategy for intersections of unions (#57871)
This commit is contained in:
parent
03c4b35255
commit
ecb6eb2354
@ -17763,6 +17763,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
removeFromEach(typeSet, TypeFlags.Null);
|
||||
result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
|
||||
}
|
||||
else if (typeSet.length >= 4) {
|
||||
// When we have four or more constituents, some of which are unions, we employ a "divide and conquer" strategy
|
||||
// where A & B & C & D is processed as (A & B) & (C & D). Since intersections of unions often produce far smaller
|
||||
// unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can
|
||||
// dramatically reduce the overall work.
|
||||
const middle = Math.floor(typeSet.length / 2);
|
||||
result = getIntersectionType([getIntersectionType(typeSet.slice(0, middle)), getIntersectionType(typeSet.slice(middle))], aliasSymbol, aliasTypeArguments);
|
||||
}
|
||||
else {
|
||||
// We are attempting to construct a type of the form X & (A | B) & (C | D). Transform this into a type of
|
||||
// the form X & A & C | X & A & D | X & B & C | X & B & D. If the estimated size of the resulting union type
|
||||
|
||||
361
tests/baselines/reference/divideAndConquerIntersections.symbols
Normal file
361
tests/baselines/reference/divideAndConquerIntersections.symbols
Normal file
@ -0,0 +1,361 @@
|
||||
//// [tests/cases/compiler/divideAndConquerIntersections.ts] ////
|
||||
|
||||
=== divideAndConquerIntersections.ts ===
|
||||
type QQ<T extends string[]> =
|
||||
>QQ : Symbol(QQ, Decl(divideAndConquerIntersections.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("a" | T[0])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("b" | T[1])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("c" | T[2])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("d" | T[3])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("e" | T[4])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("f" | T[5])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("g" | T[6])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("h" | T[7])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("i" | T[8])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("j" | T[9])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("k" | T[10])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("l" | T[11])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("m" | T[12])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("n" | T[13])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("q" | T[14])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("p" | T[15])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("q" | T[16])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("r" | T[17])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("s" | T[18])
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
& ("t" | T[19]);
|
||||
>T : Symbol(T, Decl(divideAndConquerIntersections.ts, 0, 8))
|
||||
|
||||
// Repro from #57863
|
||||
|
||||
export interface Update {
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
|
||||
update_id: number;
|
||||
>update_id : Symbol(Update.update_id, Decl(divideAndConquerIntersections.ts, 24, 25))
|
||||
|
||||
message?: { message: string };
|
||||
>message : Symbol(Update.message, Decl(divideAndConquerIntersections.ts, 25, 22))
|
||||
>message : Symbol(message, Decl(divideAndConquerIntersections.ts, 27, 15))
|
||||
|
||||
edited_message?: { edited_message: string };
|
||||
>edited_message : Symbol(Update.edited_message, Decl(divideAndConquerIntersections.ts, 27, 34))
|
||||
>edited_message : Symbol(edited_message, Decl(divideAndConquerIntersections.ts, 28, 22))
|
||||
|
||||
channel_post?: { channel_post: string };
|
||||
>channel_post : Symbol(Update.channel_post, Decl(divideAndConquerIntersections.ts, 28, 48))
|
||||
>channel_post : Symbol(channel_post, Decl(divideAndConquerIntersections.ts, 29, 20))
|
||||
|
||||
edited_channel_post?: { edited_channel_post: string };
|
||||
>edited_channel_post : Symbol(Update.edited_channel_post, Decl(divideAndConquerIntersections.ts, 29, 44))
|
||||
>edited_channel_post : Symbol(edited_channel_post, Decl(divideAndConquerIntersections.ts, 30, 27))
|
||||
|
||||
message_reaction?: { message_reaction: string };
|
||||
>message_reaction : Symbol(Update.message_reaction, Decl(divideAndConquerIntersections.ts, 30, 58))
|
||||
>message_reaction : Symbol(message_reaction, Decl(divideAndConquerIntersections.ts, 31, 24))
|
||||
|
||||
message_reaction_count?: { message_reaction_count: string };
|
||||
>message_reaction_count : Symbol(Update.message_reaction_count, Decl(divideAndConquerIntersections.ts, 31, 52))
|
||||
>message_reaction_count : Symbol(message_reaction_count, Decl(divideAndConquerIntersections.ts, 32, 30))
|
||||
|
||||
inline_query?: { inline_query: string };
|
||||
>inline_query : Symbol(Update.inline_query, Decl(divideAndConquerIntersections.ts, 32, 64))
|
||||
>inline_query : Symbol(inline_query, Decl(divideAndConquerIntersections.ts, 33, 20))
|
||||
|
||||
chosen_inline_result?: { chosen_inline_result: string };
|
||||
>chosen_inline_result : Symbol(Update.chosen_inline_result, Decl(divideAndConquerIntersections.ts, 33, 44))
|
||||
>chosen_inline_result : Symbol(chosen_inline_result, Decl(divideAndConquerIntersections.ts, 34, 28))
|
||||
|
||||
callback_query?: { callback_query: string };
|
||||
>callback_query : Symbol(Update.callback_query, Decl(divideAndConquerIntersections.ts, 34, 60))
|
||||
>callback_query : Symbol(callback_query, Decl(divideAndConquerIntersections.ts, 35, 22))
|
||||
|
||||
shipping_query?: { shipping_query: string };
|
||||
>shipping_query : Symbol(Update.shipping_query, Decl(divideAndConquerIntersections.ts, 35, 48))
|
||||
>shipping_query : Symbol(shipping_query, Decl(divideAndConquerIntersections.ts, 36, 22))
|
||||
|
||||
pre_checkout_query?: { pre_checkout_query: string };
|
||||
>pre_checkout_query : Symbol(Update.pre_checkout_query, Decl(divideAndConquerIntersections.ts, 36, 48))
|
||||
>pre_checkout_query : Symbol(pre_checkout_query, Decl(divideAndConquerIntersections.ts, 37, 26))
|
||||
|
||||
poll?: { poll: string };
|
||||
>poll : Symbol(Update.poll, Decl(divideAndConquerIntersections.ts, 37, 56))
|
||||
>poll : Symbol(poll, Decl(divideAndConquerIntersections.ts, 38, 12))
|
||||
|
||||
poll_answer?: { poll_answer: string };
|
||||
>poll_answer : Symbol(Update.poll_answer, Decl(divideAndConquerIntersections.ts, 38, 28))
|
||||
>poll_answer : Symbol(poll_answer, Decl(divideAndConquerIntersections.ts, 39, 19))
|
||||
|
||||
my_chat_member?: { my_chat_member: string };
|
||||
>my_chat_member : Symbol(Update.my_chat_member, Decl(divideAndConquerIntersections.ts, 39, 42))
|
||||
>my_chat_member : Symbol(my_chat_member, Decl(divideAndConquerIntersections.ts, 40, 22))
|
||||
|
||||
chat_member?: { chat_member: string };
|
||||
>chat_member : Symbol(Update.chat_member, Decl(divideAndConquerIntersections.ts, 40, 48))
|
||||
>chat_member : Symbol(chat_member, Decl(divideAndConquerIntersections.ts, 41, 19))
|
||||
|
||||
chat_join_request?: { chat_join_request: string };
|
||||
>chat_join_request : Symbol(Update.chat_join_request, Decl(divideAndConquerIntersections.ts, 41, 42))
|
||||
>chat_join_request : Symbol(chat_join_request, Decl(divideAndConquerIntersections.ts, 42, 25))
|
||||
|
||||
chat_boost?: { chat_boost: string };
|
||||
>chat_boost : Symbol(Update.chat_boost, Decl(divideAndConquerIntersections.ts, 42, 54))
|
||||
>chat_boost : Symbol(chat_boost, Decl(divideAndConquerIntersections.ts, 43, 18))
|
||||
|
||||
removed_chat_boost?: { removed_chat_boost: string };
|
||||
>removed_chat_boost : Symbol(Update.removed_chat_boost, Decl(divideAndConquerIntersections.ts, 43, 40))
|
||||
>removed_chat_boost : Symbol(removed_chat_boost, Decl(divideAndConquerIntersections.ts, 44, 26))
|
||||
}
|
||||
|
||||
type FilterFunction<U extends Update, V extends U> = (up: U) => up is V;
|
||||
>FilterFunction : Symbol(FilterFunction, Decl(divideAndConquerIntersections.ts, 45, 1))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 47, 20))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
>V : Symbol(V, Decl(divideAndConquerIntersections.ts, 47, 37))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 47, 20))
|
||||
>up : Symbol(up, Decl(divideAndConquerIntersections.ts, 47, 54))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 47, 20))
|
||||
>up : Symbol(up, Decl(divideAndConquerIntersections.ts, 47, 54))
|
||||
>V : Symbol(V, Decl(divideAndConquerIntersections.ts, 47, 37))
|
||||
|
||||
export function matchFilter<U extends Update, Q extends FilterQuery>(
|
||||
>matchFilter : Symbol(matchFilter, Decl(divideAndConquerIntersections.ts, 47, 72))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 49, 28))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 49, 45))
|
||||
>FilterQuery : Symbol(FilterQuery, Decl(divideAndConquerIntersections.ts, 55, 1))
|
||||
|
||||
filter: Q | Q[],
|
||||
>filter : Symbol(filter, Decl(divideAndConquerIntersections.ts, 49, 69))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 49, 45))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 49, 45))
|
||||
|
||||
): FilterFunction<U, Filter<U, Q>> {
|
||||
>FilterFunction : Symbol(FilterFunction, Decl(divideAndConquerIntersections.ts, 45, 1))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 49, 28))
|
||||
>Filter : Symbol(Filter, Decl(divideAndConquerIntersections.ts, 58, 58))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 49, 28))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 49, 45))
|
||||
|
||||
// ^ errors out
|
||||
console.log("Matching", filter);
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>filter : Symbol(filter, Decl(divideAndConquerIntersections.ts, 49, 69))
|
||||
|
||||
return (up: U): up is Filter<U, Q> => !!up;
|
||||
>up : Symbol(up, Decl(divideAndConquerIntersections.ts, 54, 12))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 49, 28))
|
||||
>up : Symbol(up, Decl(divideAndConquerIntersections.ts, 54, 12))
|
||||
>Filter : Symbol(Filter, Decl(divideAndConquerIntersections.ts, 58, 58))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 49, 28))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 49, 45))
|
||||
>up : Symbol(up, Decl(divideAndConquerIntersections.ts, 54, 12))
|
||||
}
|
||||
|
||||
/** All valid filter queries (every update key except update_id) */
|
||||
export type FilterQuery = keyof Omit<Update, "update_id">;
|
||||
>FilterQuery : Symbol(FilterQuery, Decl(divideAndConquerIntersections.ts, 55, 1))
|
||||
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
|
||||
/** Narrow down an update object based on a filter query */
|
||||
export type Filter<U extends Update, Q extends FilterQuery> = PerformQuery<
|
||||
>Filter : Symbol(Filter, Decl(divideAndConquerIntersections.ts, 58, 58))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 61, 19))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 61, 36))
|
||||
>FilterQuery : Symbol(FilterQuery, Decl(divideAndConquerIntersections.ts, 55, 1))
|
||||
>PerformQuery : Symbol(PerformQuery, Decl(divideAndConquerIntersections.ts, 75, 12))
|
||||
|
||||
U,
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 61, 19))
|
||||
|
||||
RunQuery<Q>
|
||||
>RunQuery : Symbol(RunQuery, Decl(divideAndConquerIntersections.ts, 64, 2))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 61, 36))
|
||||
|
||||
>;
|
||||
|
||||
// generate an object structure that can be intersected with updates to narrow them down
|
||||
type RunQuery<Q extends string> = Combine<L1Fragment<Q>, Q>;
|
||||
>RunQuery : Symbol(RunQuery, Decl(divideAndConquerIntersections.ts, 64, 2))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 67, 14))
|
||||
>Combine : Symbol(Combine, Decl(divideAndConquerIntersections.ts, 71, 12))
|
||||
>L1Fragment : Symbol(L1Fragment, Decl(divideAndConquerIntersections.ts, 67, 60))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 67, 14))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 67, 14))
|
||||
|
||||
// maps each part of the filter query to Record<"key", object>
|
||||
type L1Fragment<Q extends string> = Q extends unknown ? Record<Q, object>
|
||||
>L1Fragment : Symbol(L1Fragment, Decl(divideAndConquerIntersections.ts, 67, 60))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 70, 16))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 70, 16))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 70, 16))
|
||||
|
||||
: never;
|
||||
// define all other fields from query as keys with value `undefined`
|
||||
type Combine<U, K extends string> = U extends unknown
|
||||
>Combine : Symbol(Combine, Decl(divideAndConquerIntersections.ts, 71, 12))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 73, 13))
|
||||
>K : Symbol(K, Decl(divideAndConquerIntersections.ts, 73, 15))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 73, 13))
|
||||
|
||||
? U & Partial<Record<Exclude<K, keyof U>, undefined>>
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 73, 13))
|
||||
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
|
||||
>K : Symbol(K, Decl(divideAndConquerIntersections.ts, 73, 15))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 73, 13))
|
||||
|
||||
: 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
|
||||
>PerformQuery : Symbol(PerformQuery, Decl(divideAndConquerIntersections.ts, 75, 12))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 79, 18))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
>R : Symbol(R, Decl(divideAndConquerIntersections.ts, 79, 35))
|
||||
>R : Symbol(R, Decl(divideAndConquerIntersections.ts, 79, 35))
|
||||
|
||||
? FilteredEvent<U, Update & R>
|
||||
>FilteredEvent : Symbol(FilteredEvent, Decl(divideAndConquerIntersections.ts, 81, 12))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 79, 18))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
>R : Symbol(R, Decl(divideAndConquerIntersections.ts, 79, 35))
|
||||
|
||||
: never;
|
||||
|
||||
// narrow down an update by intersecting it with a different update
|
||||
type FilteredEvent<E extends Update, U extends Update> =
|
||||
>FilteredEvent : Symbol(FilteredEvent, Decl(divideAndConquerIntersections.ts, 81, 12))
|
||||
>E : Symbol(E, Decl(divideAndConquerIntersections.ts, 84, 19))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 84, 36))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
|
||||
& E
|
||||
>E : Symbol(E, Decl(divideAndConquerIntersections.ts, 84, 19))
|
||||
|
||||
& Omit<U, "update_id">;
|
||||
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 84, 36))
|
||||
|
||||
type Middleware<U extends Update> = (ctx: U) => unknown | Promise<unknown>;
|
||||
>Middleware : Symbol(Middleware, Decl(divideAndConquerIntersections.ts, 86, 27))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 88, 16))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
>ctx : Symbol(ctx, Decl(divideAndConquerIntersections.ts, 88, 37))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 88, 16))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
class EventHub<U extends Update> {
|
||||
>EventHub : Symbol(EventHub, Decl(divideAndConquerIntersections.ts, 88, 75))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 89, 15))
|
||||
>Update : Symbol(Update, Decl(divideAndConquerIntersections.ts, 20, 20))
|
||||
|
||||
use(...middleware: Array<Middleware<U>>): EventHub<U> {
|
||||
>use : Symbol(EventHub.use, Decl(divideAndConquerIntersections.ts, 89, 34))
|
||||
>middleware : Symbol(middleware, Decl(divideAndConquerIntersections.ts, 90, 8))
|
||||
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>Middleware : Symbol(Middleware, Decl(divideAndConquerIntersections.ts, 86, 27))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 89, 15))
|
||||
>EventHub : Symbol(EventHub, Decl(divideAndConquerIntersections.ts, 88, 75))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 89, 15))
|
||||
|
||||
console.log("Adding", middleware.length, "generic handlers");
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>middleware.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>middleware : Symbol(middleware, Decl(divideAndConquerIntersections.ts, 90, 8))
|
||||
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
return this;
|
||||
>this : Symbol(EventHub, Decl(divideAndConquerIntersections.ts, 88, 75))
|
||||
}
|
||||
on<Q extends FilterQuery>(
|
||||
>on : Symbol(EventHub.on, Decl(divideAndConquerIntersections.ts, 93, 5))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 94, 7))
|
||||
>FilterQuery : Symbol(FilterQuery, Decl(divideAndConquerIntersections.ts, 55, 1))
|
||||
|
||||
filter: Q | Q[],
|
||||
>filter : Symbol(filter, Decl(divideAndConquerIntersections.ts, 94, 30))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 94, 7))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 94, 7))
|
||||
|
||||
...middleware: Array<Middleware<Filter<U, Q>>>
|
||||
>middleware : Symbol(middleware, Decl(divideAndConquerIntersections.ts, 95, 24))
|
||||
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>Middleware : Symbol(Middleware, Decl(divideAndConquerIntersections.ts, 86, 27))
|
||||
>Filter : Symbol(Filter, Decl(divideAndConquerIntersections.ts, 58, 58))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 89, 15))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 94, 7))
|
||||
|
||||
// ^ errors out
|
||||
): EventHub<Filter<U, Q>> {
|
||||
>EventHub : Symbol(EventHub, Decl(divideAndConquerIntersections.ts, 88, 75))
|
||||
>Filter : Symbol(Filter, Decl(divideAndConquerIntersections.ts, 58, 58))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 89, 15))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 94, 7))
|
||||
|
||||
console.log("Adding", middleware.length, "handlers for", filter);
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>middleware.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>middleware : Symbol(middleware, Decl(divideAndConquerIntersections.ts, 95, 24))
|
||||
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>filter : Symbol(filter, Decl(divideAndConquerIntersections.ts, 94, 30))
|
||||
|
||||
return new EventHub<Filter<U, Q>>();
|
||||
>EventHub : Symbol(EventHub, Decl(divideAndConquerIntersections.ts, 88, 75))
|
||||
>Filter : Symbol(Filter, Decl(divideAndConquerIntersections.ts, 58, 58))
|
||||
>U : Symbol(U, Decl(divideAndConquerIntersections.ts, 89, 15))
|
||||
>Q : Symbol(Q, Decl(divideAndConquerIntersections.ts, 94, 7))
|
||||
}
|
||||
}
|
||||
|
||||
237
tests/baselines/reference/divideAndConquerIntersections.types
Normal file
237
tests/baselines/reference/divideAndConquerIntersections.types
Normal file
@ -0,0 +1,237 @@
|
||||
//// [tests/cases/compiler/divideAndConquerIntersections.ts] ////
|
||||
|
||||
=== Performance Stats ===
|
||||
Assignability cache: 100 / 100 (nearest 100)
|
||||
Type Count: 2,800 / 2,900 (nearest 100)
|
||||
Instantiation count: 7,500 / 7,500 (nearest 500)
|
||||
Symbol count: 26,000 / 26,000 (nearest 500)
|
||||
|
||||
=== divideAndConquerIntersections.ts ===
|
||||
type QQ<T extends string[]> =
|
||||
>QQ : QQ<T>
|
||||
|
||||
& ("a" | T[0])
|
||||
& ("b" | T[1])
|
||||
& ("c" | T[2])
|
||||
& ("d" | T[3])
|
||||
& ("e" | T[4])
|
||||
& ("f" | T[5])
|
||||
& ("g" | T[6])
|
||||
& ("h" | T[7])
|
||||
& ("i" | T[8])
|
||||
& ("j" | T[9])
|
||||
& ("k" | T[10])
|
||||
& ("l" | T[11])
|
||||
& ("m" | T[12])
|
||||
& ("n" | T[13])
|
||||
& ("q" | T[14])
|
||||
& ("p" | T[15])
|
||||
& ("q" | T[16])
|
||||
& ("r" | T[17])
|
||||
& ("s" | T[18])
|
||||
& ("t" | T[19]);
|
||||
|
||||
// Repro from #57863
|
||||
|
||||
export interface Update {
|
||||
update_id: number;
|
||||
>update_id : number
|
||||
|
||||
message?: { message: string };
|
||||
>message : { message: string; } | undefined
|
||||
>message : string
|
||||
|
||||
edited_message?: { edited_message: string };
|
||||
>edited_message : { edited_message: string; } | undefined
|
||||
>edited_message : string
|
||||
|
||||
channel_post?: { channel_post: string };
|
||||
>channel_post : { channel_post: string; } | undefined
|
||||
>channel_post : string
|
||||
|
||||
edited_channel_post?: { edited_channel_post: string };
|
||||
>edited_channel_post : { edited_channel_post: string; } | undefined
|
||||
>edited_channel_post : string
|
||||
|
||||
message_reaction?: { message_reaction: string };
|
||||
>message_reaction : { message_reaction: string; } | undefined
|
||||
>message_reaction : string
|
||||
|
||||
message_reaction_count?: { message_reaction_count: string };
|
||||
>message_reaction_count : { message_reaction_count: string; } | undefined
|
||||
>message_reaction_count : string
|
||||
|
||||
inline_query?: { inline_query: string };
|
||||
>inline_query : { inline_query: string; } | undefined
|
||||
>inline_query : string
|
||||
|
||||
chosen_inline_result?: { chosen_inline_result: string };
|
||||
>chosen_inline_result : { chosen_inline_result: string; } | undefined
|
||||
>chosen_inline_result : string
|
||||
|
||||
callback_query?: { callback_query: string };
|
||||
>callback_query : { callback_query: string; } | undefined
|
||||
>callback_query : string
|
||||
|
||||
shipping_query?: { shipping_query: string };
|
||||
>shipping_query : { shipping_query: string; } | undefined
|
||||
>shipping_query : string
|
||||
|
||||
pre_checkout_query?: { pre_checkout_query: string };
|
||||
>pre_checkout_query : { pre_checkout_query: string; } | undefined
|
||||
>pre_checkout_query : string
|
||||
|
||||
poll?: { poll: string };
|
||||
>poll : { poll: string; } | undefined
|
||||
>poll : string
|
||||
|
||||
poll_answer?: { poll_answer: string };
|
||||
>poll_answer : { poll_answer: string; } | undefined
|
||||
>poll_answer : string
|
||||
|
||||
my_chat_member?: { my_chat_member: string };
|
||||
>my_chat_member : { my_chat_member: string; } | undefined
|
||||
>my_chat_member : string
|
||||
|
||||
chat_member?: { chat_member: string };
|
||||
>chat_member : { chat_member: string; } | undefined
|
||||
>chat_member : string
|
||||
|
||||
chat_join_request?: { chat_join_request: string };
|
||||
>chat_join_request : { chat_join_request: string; } | undefined
|
||||
>chat_join_request : string
|
||||
|
||||
chat_boost?: { chat_boost: string };
|
||||
>chat_boost : { chat_boost: string; } | undefined
|
||||
>chat_boost : string
|
||||
|
||||
removed_chat_boost?: { removed_chat_boost: string };
|
||||
>removed_chat_boost : { removed_chat_boost: string; } | undefined
|
||||
>removed_chat_boost : string
|
||||
}
|
||||
|
||||
type FilterFunction<U extends Update, V extends U> = (up: U) => up is V;
|
||||
>FilterFunction : FilterFunction<U, V>
|
||||
>up : U
|
||||
|
||||
export function matchFilter<U extends Update, Q extends FilterQuery>(
|
||||
>matchFilter : <U extends Update, Q extends "message" | "edited_message" | "channel_post" | "edited_channel_post" | "message_reaction" | "message_reaction_count" | "inline_query" | "chosen_inline_result" | "callback_query" | "shipping_query" | "pre_checkout_query" | "poll" | "poll_answer" | "my_chat_member" | "chat_member" | "chat_join_request" | "chat_boost" | "removed_chat_boost">(filter: Q | Q[]) => FilterFunction<U, Filter<U, Q>>
|
||||
|
||||
filter: Q | Q[],
|
||||
>filter : Q | Q[]
|
||||
|
||||
): FilterFunction<U, Filter<U, Q>> {
|
||||
// ^ errors out
|
||||
console.log("Matching", filter);
|
||||
>console.log("Matching", filter) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>"Matching" : "Matching"
|
||||
>filter : "message" | "edited_message" | "channel_post" | "edited_channel_post" | "message_reaction" | "message_reaction_count" | "inline_query" | "chosen_inline_result" | "callback_query" | "shipping_query" | "pre_checkout_query" | "poll" | "poll_answer" | "my_chat_member" | "chat_member" | "chat_join_request" | "chat_boost" | "removed_chat_boost" | Q[]
|
||||
|
||||
return (up: U): up is Filter<U, Q> => !!up;
|
||||
>(up: U): up is Filter<U, Q> => !!up : (up: U) => up is PerformQuery<U, Combine<L1Fragment<Q>, Q>>
|
||||
>up : U
|
||||
>!!up : true
|
||||
>!up : false
|
||||
>up : U
|
||||
}
|
||||
|
||||
/** All valid filter queries (every update key except update_id) */
|
||||
export type FilterQuery = keyof Omit<Update, "update_id">;
|
||||
>FilterQuery : "message" | "edited_message" | "channel_post" | "edited_channel_post" | "message_reaction" | "message_reaction_count" | "inline_query" | "chosen_inline_result" | "callback_query" | "shipping_query" | "pre_checkout_query" | "poll" | "poll_answer" | "my_chat_member" | "chat_member" | "chat_join_request" | "chat_boost" | "removed_chat_boost"
|
||||
|
||||
/** Narrow down an update object based on a filter query */
|
||||
export type Filter<U extends Update, Q extends FilterQuery> = PerformQuery<
|
||||
>Filter : Filter<U, Q>
|
||||
|
||||
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>;
|
||||
>RunQuery : RunQuery<Q>
|
||||
|
||||
// maps each part of the filter query to Record<"key", object>
|
||||
type L1Fragment<Q extends string> = Q extends unknown ? Record<Q, object>
|
||||
>L1Fragment : L1Fragment<Q>
|
||||
|
||||
: never;
|
||||
// define all other fields from query as keys with value `undefined`
|
||||
type Combine<U, K extends string> = U extends unknown
|
||||
>Combine : Combine<U, K>
|
||||
|
||||
? 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
|
||||
>PerformQuery : PerformQuery<U, R>
|
||||
|
||||
? FilteredEvent<U, Update & R>
|
||||
: never;
|
||||
|
||||
// narrow down an update by intersecting it with a different update
|
||||
type FilteredEvent<E extends Update, U extends Update> =
|
||||
>FilteredEvent : FilteredEvent<E, U>
|
||||
|
||||
& E
|
||||
& Omit<U, "update_id">;
|
||||
|
||||
type Middleware<U extends Update> = (ctx: U) => unknown | Promise<unknown>;
|
||||
>Middleware : Middleware<U>
|
||||
>ctx : U
|
||||
|
||||
class EventHub<U extends Update> {
|
||||
>EventHub : EventHub<U>
|
||||
|
||||
use(...middleware: Array<Middleware<U>>): EventHub<U> {
|
||||
>use : (...middleware: Array<Middleware<U>>) => EventHub<U>
|
||||
>middleware : Middleware<U>[]
|
||||
|
||||
console.log("Adding", middleware.length, "generic handlers");
|
||||
>console.log("Adding", middleware.length, "generic handlers") : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>"Adding" : "Adding"
|
||||
>middleware.length : number
|
||||
>middleware : Middleware<U>[]
|
||||
>length : number
|
||||
>"generic handlers" : "generic handlers"
|
||||
|
||||
return this;
|
||||
>this : this
|
||||
}
|
||||
on<Q extends FilterQuery>(
|
||||
>on : <Q extends "message" | "edited_message" | "channel_post" | "edited_channel_post" | "message_reaction" | "message_reaction_count" | "inline_query" | "chosen_inline_result" | "callback_query" | "shipping_query" | "pre_checkout_query" | "poll" | "poll_answer" | "my_chat_member" | "chat_member" | "chat_join_request" | "chat_boost" | "removed_chat_boost">(filter: Q | Q[], ...middleware: Array<Middleware<Filter<U, Q>>>) => EventHub<Filter<U, Q>>
|
||||
|
||||
filter: Q | Q[],
|
||||
>filter : Q | Q[]
|
||||
|
||||
...middleware: Array<Middleware<Filter<U, Q>>>
|
||||
>middleware : Middleware<PerformQuery<U, Combine<L1Fragment<Q>, Q>>>[]
|
||||
|
||||
// ^ errors out
|
||||
): EventHub<Filter<U, Q>> {
|
||||
console.log("Adding", middleware.length, "handlers for", filter);
|
||||
>console.log("Adding", middleware.length, "handlers for", filter) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>"Adding" : "Adding"
|
||||
>middleware.length : number
|
||||
>middleware : Middleware<PerformQuery<U, Combine<L1Fragment<Q>, Q>>>[]
|
||||
>length : number
|
||||
>"handlers for" : "handlers for"
|
||||
>filter : "message" | "edited_message" | "channel_post" | "edited_channel_post" | "message_reaction" | "message_reaction_count" | "inline_query" | "chosen_inline_result" | "callback_query" | "shipping_query" | "pre_checkout_query" | "poll" | "poll_answer" | "my_chat_member" | "chat_member" | "chat_join_request" | "chat_boost" | "removed_chat_boost" | Q[]
|
||||
|
||||
return new EventHub<Filter<U, Q>>();
|
||||
>new EventHub<Filter<U, Q>>() : EventHub<PerformQuery<U, Combine<L1Fragment<Q>, Q>>>
|
||||
>EventHub : typeof EventHub
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
//// [tests/cases/compiler/normalizedIntersectionTooComplex.ts] ////
|
||||
|
||||
=== Performance Stats ===
|
||||
Assignability cache: 300 / 300 (nearest 100)
|
||||
Type Count: 1,500 / 1,500 (nearest 100)
|
||||
Instantiation count: 500 / 500 (nearest 500)
|
||||
Symbol count: 25,500 / 25,500 (nearest 500)
|
||||
|
||||
=== normalizedIntersectionTooComplex.ts ===
|
||||
// Repro from #30050
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
=== Performance Stats ===
|
||||
Identity cache: 200 / 200 (nearest 100)
|
||||
Assignability cache: 13,300 / 13,500 (nearest 100)
|
||||
Type Count: 27,200 / 27,600 (nearest 100)
|
||||
Type Count: 27,300 / 27,600 (nearest 100)
|
||||
Instantiation count: 374,500 / 375,500 (nearest 500)
|
||||
Symbol count: 92,000 / 92,000 (nearest 500)
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
=== Performance Stats ===
|
||||
Assignability cache: 900 / 900 (nearest 100)
|
||||
Type Count: 15,000 / 15,000 (nearest 100)
|
||||
Type Count: 16,100 / 16,100 (nearest 100)
|
||||
Instantiation count: 3,500 / 3,500 (nearest 500)
|
||||
Symbol count: 27,000 / 27,500 (nearest 500)
|
||||
|
||||
|
||||
106
tests/cases/compiler/divideAndConquerIntersections.ts
Normal file
106
tests/cases/compiler/divideAndConquerIntersections.ts
Normal file
@ -0,0 +1,106 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
type QQ<T extends string[]> =
|
||||
& ("a" | T[0])
|
||||
& ("b" | T[1])
|
||||
& ("c" | T[2])
|
||||
& ("d" | T[3])
|
||||
& ("e" | T[4])
|
||||
& ("f" | T[5])
|
||||
& ("g" | T[6])
|
||||
& ("h" | T[7])
|
||||
& ("i" | T[8])
|
||||
& ("j" | T[9])
|
||||
& ("k" | T[10])
|
||||
& ("l" | T[11])
|
||||
& ("m" | T[12])
|
||||
& ("n" | T[13])
|
||||
& ("q" | T[14])
|
||||
& ("p" | T[15])
|
||||
& ("q" | T[16])
|
||||
& ("r" | T[17])
|
||||
& ("s" | T[18])
|
||||
& ("t" | T[19]);
|
||||
|
||||
// Repro from #57863
|
||||
|
||||
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>>();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user