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.
This commit is contained in:
Nathan Shively-Sanders 2017-12-14 13:33:02 -08:00
parent 7d1a980ad2
commit 05de0a7da3
3 changed files with 37 additions and 9 deletions

View File

@ -335,6 +335,7 @@ namespace ts {
const globals = createSymbolTable();
const deferredInferenceCache = createMap<Type | undefined>();
const deferredMappedTypeInstantiationStack: string[] = [];
let ambientModulesCache: Symbol[] | undefined;
/**
* List of every ambient module with a "*" wildcard.
@ -2867,7 +2868,23 @@ namespace ts {
}
for (const propertySymbol of properties) {
const propertyType = getTypeOfSymbol(propertySymbol);
let propertyType: Type;
if (getCheckFlags(propertySymbol) & CheckFlags.DeferredInferred) {
const deferred = propertySymbol as DeferredTransientSymbol;
const key = deferred.propertyType.id + "," + (deferred.mappedType.symbol ? deferred.mappedType.symbol.id : "");
// Temporary solution to recursive printing: zero out repeated types.
if (contains(deferredMappedTypeInstantiationStack, key)) {
// TODO: Could probably be an actual cache that returns {} when it contains undefined.
propertyType = emptyObjectType;
}
else {
deferredMappedTypeInstantiationStack.push(key)
propertyType = getTypeOfSymbol(propertySymbol);
}
}
else {
propertyType = getTypeOfSymbol(propertySymbol);
}
const saveEnclosingDeclaration = context.enclosingDeclaration;
context.enclosingDeclaration = undefined;
const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true);
@ -2894,6 +2911,9 @@ namespace ts {
/*initializer*/ undefined);
typeElements.push(propertySignature);
}
if (getCheckFlags(propertySymbol) & CheckFlags.DeferredInferred) {
deferredMappedTypeInstantiationStack.pop();
}
}
return typeElements.length ? typeElements : undefined;
}
@ -4869,7 +4889,7 @@ namespace ts {
return getTypeOfInstantiatedSymbol(symbol);
}
if (getCheckFlags(symbol) & CheckFlags.DeferredInferred) {
return inferTargetType((symbol as DeferredTransientSymbol).propertyType, (symbol as DeferredTransientSymbol).mappedType);
return inferDeferredMappedType((symbol as DeferredTransientSymbol).propertyType, (symbol as DeferredTransientSymbol).mappedType);
}
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
return getTypeOfVariableOrParameterOrProperty(symbol);
@ -11233,7 +11253,13 @@ namespace ts {
if (deferredInferenceCache.has(key)) {
return deferredInferenceCache.get(key);
}
deferredInferenceCache.set(key, function() {
deferredInferenceCache.set(key, undefined);
const type = createDeferredMappedType(source, target);
deferredInferenceCache.set(key, type);
return type;
}
function createDeferredMappedType(source: Type, target: MappedType) {
const properties = getPropertiesOfType(source);
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
if (properties.length === 0 && !indexInfo) {
@ -11257,14 +11283,15 @@ namespace ts {
members.set(prop.escapedName, inferredProp);
}
if (indexInfo) {
// TODO: Defer this too. BARREL OF LAUGHS RIGHT THERE
indexInfo = createIndexInfo(inferTargetType(indexInfo.type, target), readonlyMask && indexInfo.isReadonly);
// TODO: Defer this too.
// (probably the simplest way is to have a special type that defers the creation of (at least) its index info in
// resolveStructuredTypeMembers
indexInfo = createIndexInfo(inferDeferredMappedType(indexInfo.type, target), readonlyMask && indexInfo.isReadonly);
}
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
}());
}
function inferTargetType(sourceType: Type, target: MappedType): Type {
function inferDeferredMappedType(sourceType: Type, target: MappedType): Type {
const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
const templateType = getTemplateTypeFromMappedType(target);
const inference = createInferenceInfo(typeParameter);

View File

@ -3227,6 +3227,7 @@ namespace ts {
isRestParameter?: boolean;
}
/* @internal */
export interface DeferredTransientSymbol extends TransientSymbol {
propertyType: Type;
mappedType: MappedType;

View File

@ -26,8 +26,8 @@ declare function foo<T>(deep: Deep<T>): T;
>T : T
const out = foo(a);
>out : { a: {}; }
>foo(a) : { a: {}; }
>out : { a: { a: {}; }; }
>foo(a) : { a: { a: {}; }; }
>foo : <T>(deep: Deep<T>) => T
>a : A