Fixed regression in reverse mapped type inference caused by cache leak (#59232)

Co-authored-by: Gabriela Araujo Britto <gabrielaa@microsoft.com>
This commit is contained in:
Mateusz Burzyński 2024-07-13 00:50:43 +02:00 коммит произвёл GitHub
Родитель 9c093c13e6
Коммит 6d3be985c8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 300 добавлений и 13 удалений

Просмотреть файл

@ -2194,6 +2194,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
/** Key is "/path/to/a.ts|/path/to/b.ts". */
var amalgamatedDuplicates: Map<string, DuplicateInfoForFiles> | undefined;
var reverseMappedCache = new Map<string, Type | undefined>();
var reverseHomomorphicMappedCache = new Map<string, Type | undefined>();
var ambientModulesCache: Symbol[] | undefined;
/**
* List of every ambient module with a "*" wildcard.
@ -7030,12 +7031,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function shouldUsePlaceholderForProperty(propertySymbol: Symbol, context: NodeBuilderContext) {
// Use placeholders for reverse mapped types we've either already descended into, or which
// are nested reverse mappings within a mapping over a non-anonymous type. The later is a restriction mostly just to
// Use placeholders for reverse mapped types we've either
// (1) already descended into, or
// (2) are nested reverse mappings within a mapping over a non-anonymous type, or
// (3) are deeply nested properties that originate from the same mapped type.
// Condition (2) is a restriction mostly just to
// reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`.
// Since anonymous types usually come from expressions, this allows us to preserve the output
// for deep mappings which likely come from expressions, while truncating those parts which
// come from mappings over library functions.
// Condition (3) limits printing of possibly infinitely deep reverse mapped types.
const depth = 3;
return !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped)
&& (
contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol)
@ -7043,7 +7049,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
context.reverseMappedStack?.[0]
&& !(getObjectFlags(last(context.reverseMappedStack).links.propertyType) & ObjectFlags.Anonymous)
)
|| isDeeplyNestedReverseMappedTypeProperty()
);
function isDeeplyNestedReverseMappedTypeProperty() {
if ((context.reverseMappedStack?.length ?? 0) < depth) {
return false;
}
for (let i = 0; i < depth; i++) {
const prop = context.reverseMappedStack![context.reverseMappedStack!.length - 1 - i];
if (prop.links.mappedType.symbol !== (propertySymbol as ReverseMappedSymbol).links.mappedType.symbol) {
return false;
}
}
return true;
}
}
function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) {
@ -25657,11 +25676,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
*/
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, constraint: IndexType): Type | undefined {
const cacheKey = source.id + "," + target.id + "," + constraint.id;
if (reverseMappedCache.has(cacheKey)) {
return reverseMappedCache.get(cacheKey);
if (reverseHomomorphicMappedCache.has(cacheKey)) {
return reverseHomomorphicMappedCache.get(cacheKey);
}
const type = createReverseMappedType(source, target, constraint);
reverseMappedCache.set(cacheKey, type);
reverseHomomorphicMappedCache.set(cacheKey, type);
return type;
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -2,8 +2,8 @@
=== Performance Stats ===
Assignability cache: 5,000
Type Count: 10,000
Instantiation count: 250,000
Type Count: 25,000
Instantiation count: 500,000
Symbol count: 250,000
=== mappedTypeRecursiveInference.ts ===

Просмотреть файл

@ -0,0 +1,117 @@
//// [tests/cases/compiler/reverseMappedTypeInferenceSameSource1.ts] ////
=== reverseMappedTypeInferenceSameSource1.ts ===
type Action<T extends string = string> = {
>Action : Symbol(Action, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 0))
>T : Symbol(T, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 12))
type: T;
>type : Symbol(type, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 42))
>T : Symbol(T, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 12))
};
interface UnknownAction extends Action {
>UnknownAction : Symbol(UnknownAction, Decl(reverseMappedTypeInferenceSameSource1.ts, 2, 2))
>Action : Symbol(Action, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 0))
[extraProps: string]: unknown;
>extraProps : Symbol(extraProps, Decl(reverseMappedTypeInferenceSameSource1.ts, 4, 3))
}
type Reducer<S = any, A extends Action = UnknownAction> = (
>Reducer : Symbol(Reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 5, 1))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 6, 13))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 6, 21))
>Action : Symbol(Action, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 0))
>UnknownAction : Symbol(UnknownAction, Decl(reverseMappedTypeInferenceSameSource1.ts, 2, 2))
state: S | undefined,
>state : Symbol(state, Decl(reverseMappedTypeInferenceSameSource1.ts, 6, 59))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 6, 13))
action: A,
>action : Symbol(action, Decl(reverseMappedTypeInferenceSameSource1.ts, 7, 23))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 6, 21))
) => S;
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 6, 13))
type ReducersMapObject<S = any, A extends Action = UnknownAction> = {
>ReducersMapObject : Symbol(ReducersMapObject, Decl(reverseMappedTypeInferenceSameSource1.ts, 9, 7))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 11, 23))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 11, 31))
>Action : Symbol(Action, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 0))
>UnknownAction : Symbol(UnknownAction, Decl(reverseMappedTypeInferenceSameSource1.ts, 2, 2))
[K in keyof S]: Reducer<S[K], A>;
>K : Symbol(K, Decl(reverseMappedTypeInferenceSameSource1.ts, 12, 3))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 11, 23))
>Reducer : Symbol(Reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 5, 1))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 11, 23))
>K : Symbol(K, Decl(reverseMappedTypeInferenceSameSource1.ts, 12, 3))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 11, 31))
};
interface ConfigureStoreOptions<S = any, A extends Action = UnknownAction> {
>ConfigureStoreOptions : Symbol(ConfigureStoreOptions, Decl(reverseMappedTypeInferenceSameSource1.ts, 13, 2))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 15, 32))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 15, 40))
>Action : Symbol(Action, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 0))
>UnknownAction : Symbol(UnknownAction, Decl(reverseMappedTypeInferenceSameSource1.ts, 2, 2))
reducer: Reducer<S, A> | ReducersMapObject<S, A>;
>reducer : Symbol(ConfigureStoreOptions.reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 15, 76))
>Reducer : Symbol(Reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 5, 1))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 15, 32))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 15, 40))
>ReducersMapObject : Symbol(ReducersMapObject, Decl(reverseMappedTypeInferenceSameSource1.ts, 9, 7))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 15, 32))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 15, 40))
}
declare function configureStore<S = any, A extends Action = UnknownAction>(
>configureStore : Symbol(configureStore, Decl(reverseMappedTypeInferenceSameSource1.ts, 17, 1))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 19, 32))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 19, 40))
>Action : Symbol(Action, Decl(reverseMappedTypeInferenceSameSource1.ts, 0, 0))
>UnknownAction : Symbol(UnknownAction, Decl(reverseMappedTypeInferenceSameSource1.ts, 2, 2))
options: ConfigureStoreOptions<S, A>,
>options : Symbol(options, Decl(reverseMappedTypeInferenceSameSource1.ts, 19, 75))
>ConfigureStoreOptions : Symbol(ConfigureStoreOptions, Decl(reverseMappedTypeInferenceSameSource1.ts, 13, 2))
>S : Symbol(S, Decl(reverseMappedTypeInferenceSameSource1.ts, 19, 32))
>A : Symbol(A, Decl(reverseMappedTypeInferenceSameSource1.ts, 19, 40))
): void;
{
const reducer: Reducer<number> = () => 0;
>reducer : Symbol(reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 24, 7))
>Reducer : Symbol(Reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 5, 1))
const store1 = configureStore({ reducer });
>store1 : Symbol(store1, Decl(reverseMappedTypeInferenceSameSource1.ts, 25, 7))
>configureStore : Symbol(configureStore, Decl(reverseMappedTypeInferenceSameSource1.ts, 17, 1))
>reducer : Symbol(reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 25, 33))
}
const counterReducer1: Reducer<number> = () => 0;
>counterReducer1 : Symbol(counterReducer1, Decl(reverseMappedTypeInferenceSameSource1.ts, 28, 5))
>Reducer : Symbol(Reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 5, 1))
const store2 = configureStore({
>store2 : Symbol(store2, Decl(reverseMappedTypeInferenceSameSource1.ts, 30, 5))
>configureStore : Symbol(configureStore, Decl(reverseMappedTypeInferenceSameSource1.ts, 17, 1))
reducer: {
>reducer : Symbol(reducer, Decl(reverseMappedTypeInferenceSameSource1.ts, 30, 31))
counter1: counterReducer1,
>counter1 : Symbol(counter1, Decl(reverseMappedTypeInferenceSameSource1.ts, 31, 12))
>counterReducer1 : Symbol(counterReducer1, Decl(reverseMappedTypeInferenceSameSource1.ts, 28, 5))
},
});
export {}

Просмотреть файл

@ -0,0 +1,111 @@
//// [tests/cases/compiler/reverseMappedTypeInferenceSameSource1.ts] ////
=== reverseMappedTypeInferenceSameSource1.ts ===
type Action<T extends string = string> = {
>Action : Action<T>
> : ^^^^^^^^^
type: T;
>type : T
> : ^
};
interface UnknownAction extends Action {
[extraProps: string]: unknown;
>extraProps : string
> : ^^^^^^
}
type Reducer<S = any, A extends Action = UnknownAction> = (
>Reducer : Reducer<S, A>
> : ^^^^^^^^^^^^^
state: S | undefined,
>state : S | undefined
> : ^^^^^^^^^^^^^
action: A,
>action : A
> : ^
) => S;
type ReducersMapObject<S = any, A extends Action = UnknownAction> = {
>ReducersMapObject : ReducersMapObject<S, A>
> : ^^^^^^^^^^^^^^^^^^^^^^^
[K in keyof S]: Reducer<S[K], A>;
};
interface ConfigureStoreOptions<S = any, A extends Action = UnknownAction> {
reducer: Reducer<S, A> | ReducersMapObject<S, A>;
>reducer : Reducer<S, A> | ReducersMapObject<S, A>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
declare function configureStore<S = any, A extends Action = UnknownAction>(
>configureStore : <S = any, A extends Action = UnknownAction>(options: ConfigureStoreOptions<S, A>) => void
> : ^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^ ^^^^^
options: ConfigureStoreOptions<S, A>,
>options : ConfigureStoreOptions<S, A>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^
): void;
{
const reducer: Reducer<number> = () => 0;
>reducer : Reducer<number, UnknownAction>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>() => 0 : () => number
> : ^^^^^^^^^^^^
>0 : 0
> : ^
const store1 = configureStore({ reducer });
>store1 : void
> : ^^^^
>configureStore({ reducer }) : void
> : ^^^^
>configureStore : <S = any, A extends Action = UnknownAction>(options: ConfigureStoreOptions<S, A>) => void
> : ^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^ ^^^^^
>{ reducer } : { reducer: Reducer<number, UnknownAction>; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>reducer : Reducer<number, UnknownAction>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
const counterReducer1: Reducer<number> = () => 0;
>counterReducer1 : Reducer<number, UnknownAction>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>() => 0 : () => number
> : ^^^^^^^^^^^^
>0 : 0
> : ^
const store2 = configureStore({
>store2 : void
> : ^^^^
>configureStore({ reducer: { counter1: counterReducer1, },}) : void
> : ^^^^
>configureStore : <S = any, A extends Action = UnknownAction>(options: ConfigureStoreOptions<S, A>) => void
> : ^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^ ^^^^^
>{ reducer: { counter1: counterReducer1, },} : { reducer: { counter1: Reducer<number, UnknownAction>; }; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
reducer: {
>reducer : { counter1: Reducer<number, UnknownAction>; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>{ counter1: counterReducer1, } : { counter1: Reducer<number, UnknownAction>; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
counter1: counterReducer1,
>counter1 : Reducer<number, UnknownAction>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>counterReducer1 : Reducer<number, UnknownAction>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
},
});
export {}

Просмотреть файл

@ -0,0 +1,40 @@
// @strict: true
// @noEmit: true
type Action<T extends string = string> = {
type: T;
};
interface UnknownAction extends Action {
[extraProps: string]: unknown;
}
type Reducer<S = any, A extends Action = UnknownAction> = (
state: S | undefined,
action: A,
) => S;
type ReducersMapObject<S = any, A extends Action = UnknownAction> = {
[K in keyof S]: Reducer<S[K], A>;
};
interface ConfigureStoreOptions<S = any, A extends Action = UnknownAction> {
reducer: Reducer<S, A> | ReducersMapObject<S, A>;
}
declare function configureStore<S = any, A extends Action = UnknownAction>(
options: ConfigureStoreOptions<S, A>,
): void;
{
const reducer: Reducer<number> = () => 0;
const store1 = configureStore({ reducer });
}
const counterReducer1: Reducer<number> = () => 0;
const store2 = configureStore({
reducer: {
counter1: counterReducer1,
},
});
export {}