@@ -339,6 +339,7 @@ namespace ts {
339
339
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
340
340
341
341
const globals = createSymbolTable();
342
+ const reverseMappedCache = createMap<Type | undefined>();
342
343
let ambientModulesCache: Symbol[] | undefined;
343
344
/**
344
345
* List of every ambient module with a "*" wildcard.
@@ -2860,7 +2861,10 @@ namespace ts {
2860
2861
typeElements.push(<ConstructSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context));
2861
2862
}
2862
2863
if (resolvedType.stringIndexInfo) {
2863
- typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context));
2864
+ const indexInfo = resolvedType.objectFlags & ObjectFlags.ReverseMapped ?
2865
+ createIndexInfo(anyType, resolvedType.stringIndexInfo.isReadonly, resolvedType.stringIndexInfo.declaration) :
2866
+ resolvedType.stringIndexInfo;
2867
+ typeElements.push(indexInfoToIndexSignatureDeclarationHelper(indexInfo, IndexKind.String, context));
2864
2868
}
2865
2869
if (resolvedType.numberIndexInfo) {
2866
2870
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context));
@@ -2872,7 +2876,7 @@ namespace ts {
2872
2876
}
2873
2877
2874
2878
for (const propertySymbol of properties) {
2875
- const propertyType = getTypeOfSymbol(propertySymbol);
2879
+ const propertyType = getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped ? anyType : getTypeOfSymbol(propertySymbol);
2876
2880
const saveEnclosingDeclaration = context.enclosingDeclaration;
2877
2881
context.enclosingDeclaration = undefined;
2878
2882
const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true);
@@ -3681,7 +3685,10 @@ namespace ts {
3681
3685
writePunctuation(writer, SyntaxKind.SemicolonToken);
3682
3686
writer.writeLine();
3683
3687
}
3684
- buildIndexSignatureDisplay(resolved.stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack);
3688
+ const stringIndexInfo = resolved.objectFlags & ObjectFlags.ReverseMapped && resolved.stringIndexInfo ?
3689
+ createIndexInfo(anyType, resolved.stringIndexInfo.isReadonly, resolved.stringIndexInfo.declaration) :
3690
+ resolved.stringIndexInfo;
3691
+ buildIndexSignatureDisplay(stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack);
3685
3692
buildIndexSignatureDisplay(resolved.numberIndexInfo, writer, IndexKind.Number, enclosingDeclaration, globalFlags, symbolStack);
3686
3693
for (const p of resolved.properties) {
3687
3694
if (globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) {
@@ -3692,7 +3699,7 @@ namespace ts {
3692
3699
writer.reportPrivateInBaseOfClassExpression(symbolName(p));
3693
3700
}
3694
3701
}
3695
- const t = getTypeOfSymbol(p);
3702
+ const t = getCheckFlags(p) & CheckFlags.ReverseMapped ? anyType : getTypeOfSymbol(p);
3696
3703
if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) {
3697
3704
const signatures = getSignaturesOfType(t, SignatureKind.Call);
3698
3705
for (const signature of signatures) {
@@ -4900,6 +4907,9 @@ namespace ts {
4900
4907
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
4901
4908
return getTypeOfInstantiatedSymbol(symbol);
4902
4909
}
4910
+ if (getCheckFlags(symbol) & CheckFlags.ReverseMapped) {
4911
+ return getTypeOfReverseMappedSymbol(symbol as ReverseMappedSymbol);
4912
+ }
4903
4913
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
4904
4914
return getTypeOfVariableOrParameterOrProperty(symbol);
4905
4915
}
@@ -6110,6 +6120,23 @@ namespace ts {
6110
6120
}
6111
6121
}
6112
6122
6123
+ function resolveReverseMappedTypeMembers(type: ReverseMappedType) {
6124
+ const indexInfo = getIndexInfoOfType(type.source, IndexKind.String);
6125
+ const readonlyMask = type.mappedType.declaration.readonlyToken ? false : true;
6126
+ const optionalMask = type.mappedType.declaration.questionToken ? 0 : SymbolFlags.Optional;
6127
+ const stringIndexInfo = indexInfo && createIndexInfo(inferReverseMappedType(indexInfo.type, type.mappedType), readonlyMask && indexInfo.isReadonly);
6128
+ const members = createSymbolTable();
6129
+ for (const prop of getPropertiesOfType(type.source)) {
6130
+ const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0);
6131
+ const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol;
6132
+ inferredProp.declarations = prop.declarations;
6133
+ inferredProp.propertyType = getTypeOfSymbol(prop);
6134
+ inferredProp.mappedType = type.mappedType;
6135
+ members.set(prop.escapedName, inferredProp);
6136
+ }
6137
+ setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
6138
+ }
6139
+
6113
6140
/** Resolve the members of a mapped type { [P in K]: T } */
6114
6141
function resolveMappedTypeMembers(type: MappedType) {
6115
6142
const members: SymbolTable = createSymbolTable();
@@ -6249,6 +6276,9 @@ namespace ts {
6249
6276
else if ((<ObjectType>type).objectFlags & ObjectFlags.ClassOrInterface) {
6250
6277
resolveClassOrInterfaceMembers(<InterfaceType>type);
6251
6278
}
6279
+ else if ((<ReverseMappedType>type).objectFlags & ObjectFlags.ReverseMapped) {
6280
+ resolveReverseMappedTypeMembers(type as ReverseMappedType);
6281
+ }
6252
6282
else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
6253
6283
resolveAnonymousTypeMembers(<AnonymousType>type);
6254
6284
}
@@ -11275,42 +11305,45 @@ namespace ts {
11275
11305
* property is computed by inferring from the source property type to X for the type
11276
11306
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
11277
11307
*/
11278
- function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type {
11308
+ function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
11309
+ const key = source.id + "," + target.id;
11310
+ if (reverseMappedCache.has(key)) {
11311
+ return reverseMappedCache.get(key);
11312
+ }
11313
+ reverseMappedCache.set(key, undefined);
11314
+ const type = createReverseMappedType(source, target);
11315
+ reverseMappedCache.set(key, type);
11316
+ return type;
11317
+ }
11318
+
11319
+ function createReverseMappedType(source: Type, target: MappedType) {
11279
11320
const properties = getPropertiesOfType(source);
11280
- let indexInfo = getIndexInfoOfType(source, IndexKind.String);
11281
- if (properties.length === 0 && !indexInfo) {
11321
+ if (properties.length === 0 && !getIndexInfoOfType(source, IndexKind.String)) {
11282
11322
return undefined;
11283
11323
}
11284
- const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11285
- const inference = createInferenceInfo(typeParameter);
11286
- const inferences = [inference];
11287
- const templateType = getTemplateTypeFromMappedType(target);
11288
- const readonlyMask = target.declaration.readonlyToken ? false : true;
11289
- const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
11290
- const members = createSymbolTable();
11324
+ // If any property contains context sensitive functions that have been skipped, the source type
11325
+ // is incomplete and we can't infer a meaningful input type.
11291
11326
for (const prop of properties) {
11292
- const propType = getTypeOfSymbol(prop);
11293
- // If any property contains context sensitive functions that have been skipped, the source type
11294
- // is incomplete and we can't infer a meaningful input type.
11295
- if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
11327
+ if (getTypeOfSymbol(prop).flags & TypeFlags.ContainsAnyFunctionType) {
11296
11328
return undefined;
11297
11329
}
11298
- const checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
11299
- const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags);
11300
- inferredProp.declarations = prop.declarations;
11301
- inferredProp.type = inferTargetType(propType);
11302
- members.set(prop.escapedName, inferredProp);
11303
11330
}
11304
- if (indexInfo) {
11305
- indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly);
11306
- }
11307
- return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
11331
+ const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType;
11332
+ reversed.source = source;
11333
+ reversed.mappedType = target;
11334
+ return reversed;
11335
+ }
11308
11336
11309
- function inferTargetType(sourceType: Type): Type {
11310
- inference.candidates = undefined;
11311
- inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
11312
- return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
11313
- }
11337
+ function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) {
11338
+ return inferReverseMappedType(symbol.propertyType, symbol.mappedType);
11339
+ }
11340
+
11341
+ function inferReverseMappedType(sourceType: Type, target: MappedType): Type {
11342
+ const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11343
+ const templateType = getTemplateTypeFromMappedType(target);
11344
+ const inference = createInferenceInfo(typeParameter);
11345
+ inferTypes([inference], sourceType, templateType);
11346
+ return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
11314
11347
}
11315
11348
11316
11349
function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) {
@@ -11326,7 +11359,7 @@ namespace ts {
11326
11359
return undefined;
11327
11360
}
11328
11361
11329
- function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: string[] ) {
11362
+ function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
11330
11363
let symbolStack: Symbol[];
11331
11364
let visited: Map<boolean>;
11332
11365
inferFromTypes(originalSource, originalTarget);
@@ -11543,13 +11576,7 @@ namespace ts {
11543
11576
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
11544
11577
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
11545
11578
if (inference && !inference.isFixed) {
11546
- const key = (source.symbol ? getSymbolId(source.symbol) + "," : "") + getSymbolId(target.symbol);
11547
- if (contains(mappedTypeStack, key)) {
11548
- return;
11549
- }
11550
- (mappedTypeStack || (mappedTypeStack = [])).push(key);
11551
- const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack);
11552
- mappedTypeStack.pop();
11579
+ const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
11553
11580
if (inferredType) {
11554
11581
const savePriority = priority;
11555
11582
priority |= InferencePriority.MappedType;
0 commit comments