Skip to content

Commit 05de0a7

Browse files
committed
Get it working:
1. Actually return the cached result! 2. Unnest worker function. 3. Improve all the names. 4. Pre-set the cache to undefined to avoid loops. (Not sure this is needed, though.) 5. Make the new type internal to avoid baseline changes. 6. Cut off recursion in the printing of recursive deferred mapped types. Note that (6) required introducing a new stack that is exactly like mappedTypeStack. I think the cache may actually be needed here, not in the creation of the deferred type.
1 parent 7d1a980 commit 05de0a7

3 files changed

Lines changed: 37 additions & 9 deletions

File tree

src/compiler/checker.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ namespace ts {
335335

336336
const globals = createSymbolTable();
337337
const deferredInferenceCache = createMap<Type | undefined>();
338+
const deferredMappedTypeInstantiationStack: string[] = [];
338339
let ambientModulesCache: Symbol[] | undefined;
339340
/**
340341
* List of every ambient module with a "*" wildcard.
@@ -2867,7 +2868,23 @@ namespace ts {
28672868
}
28682869

28692870
for (const propertySymbol of properties) {
2870-
const propertyType = getTypeOfSymbol(propertySymbol);
2871+
let propertyType: Type;
2872+
if (getCheckFlags(propertySymbol) & CheckFlags.DeferredInferred) {
2873+
const deferred = propertySymbol as DeferredTransientSymbol;
2874+
const key = deferred.propertyType.id + "," + (deferred.mappedType.symbol ? deferred.mappedType.symbol.id : "");
2875+
// Temporary solution to recursive printing: zero out repeated types.
2876+
if (contains(deferredMappedTypeInstantiationStack, key)) {
2877+
// TODO: Could probably be an actual cache that returns {} when it contains undefined.
2878+
propertyType = emptyObjectType;
2879+
}
2880+
else {
2881+
deferredMappedTypeInstantiationStack.push(key)
2882+
propertyType = getTypeOfSymbol(propertySymbol);
2883+
}
2884+
}
2885+
else {
2886+
propertyType = getTypeOfSymbol(propertySymbol);
2887+
}
28712888
const saveEnclosingDeclaration = context.enclosingDeclaration;
28722889
context.enclosingDeclaration = undefined;
28732890
const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true);
@@ -2894,6 +2911,9 @@ namespace ts {
28942911
/*initializer*/ undefined);
28952912
typeElements.push(propertySignature);
28962913
}
2914+
if (getCheckFlags(propertySymbol) & CheckFlags.DeferredInferred) {
2915+
deferredMappedTypeInstantiationStack.pop();
2916+
}
28972917
}
28982918
return typeElements.length ? typeElements : undefined;
28992919
}
@@ -4869,7 +4889,7 @@ namespace ts {
48694889
return getTypeOfInstantiatedSymbol(symbol);
48704890
}
48714891
if (getCheckFlags(symbol) & CheckFlags.DeferredInferred) {
4872-
return inferTargetType((symbol as DeferredTransientSymbol).propertyType, (symbol as DeferredTransientSymbol).mappedType);
4892+
return inferDeferredMappedType((symbol as DeferredTransientSymbol).propertyType, (symbol as DeferredTransientSymbol).mappedType);
48734893
}
48744894
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
48754895
return getTypeOfVariableOrParameterOrProperty(symbol);
@@ -11233,7 +11253,13 @@ namespace ts {
1123311253
if (deferredInferenceCache.has(key)) {
1123411254
return deferredInferenceCache.get(key);
1123511255
}
11236-
deferredInferenceCache.set(key, function() {
11256+
deferredInferenceCache.set(key, undefined);
11257+
const type = createDeferredMappedType(source, target);
11258+
deferredInferenceCache.set(key, type);
11259+
return type;
11260+
}
11261+
11262+
function createDeferredMappedType(source: Type, target: MappedType) {
1123711263
const properties = getPropertiesOfType(source);
1123811264
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
1123911265
if (properties.length === 0 && !indexInfo) {
@@ -11257,14 +11283,15 @@ namespace ts {
1125711283
members.set(prop.escapedName, inferredProp);
1125811284
}
1125911285
if (indexInfo) {
11260-
// TODO: Defer this too. BARREL OF LAUGHS RIGHT THERE
11261-
indexInfo = createIndexInfo(inferTargetType(indexInfo.type, target), readonlyMask && indexInfo.isReadonly);
11286+
// TODO: Defer this too.
11287+
// (probably the simplest way is to have a special type that defers the creation of (at least) its index info in
11288+
// resolveStructuredTypeMembers
11289+
indexInfo = createIndexInfo(inferDeferredMappedType(indexInfo.type, target), readonlyMask && indexInfo.isReadonly);
1126211290
}
1126311291
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
11264-
}());
1126511292
}
1126611293

11267-
function inferTargetType(sourceType: Type, target: MappedType): Type {
11294+
function inferDeferredMappedType(sourceType: Type, target: MappedType): Type {
1126811295
const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
1126911296
const templateType = getTemplateTypeFromMappedType(target);
1127011297
const inference = createInferenceInfo(typeParameter);

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3227,6 +3227,7 @@ namespace ts {
32273227
isRestParameter?: boolean;
32283228
}
32293229

3230+
/* @internal */
32303231
export interface DeferredTransientSymbol extends TransientSymbol {
32313232
propertyType: Type;
32323233
mappedType: MappedType;

tests/baselines/reference/mappedTypeRecursiveInference.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ declare function foo<T>(deep: Deep<T>): T;
2626
>T : T
2727

2828
const out = foo(a);
29-
>out : { a: {}; }
30-
>foo(a) : { a: {}; }
29+
>out : { a: { a: {}; }; }
30+
>foo(a) : { a: { a: {}; }; }
3131
>foo : <T>(deep: Deep<T>) => T
3232
>a : A
3333

0 commit comments

Comments
 (0)