@@ -5788,8 +5788,7 @@ namespace ts {
5788
5788
}
5789
5789
5790
5790
function isGenericMappedType(type: Type) {
5791
- return getObjectFlags(type) & ObjectFlags.Mapped &&
5792
- maybeTypeOfKind(getConstraintTypeFromMappedType(<MappedType>type), TypeFlags.TypeVariable | TypeFlags.Index);
5791
+ return getObjectFlags(type) & ObjectFlags.Mapped && isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type));
5793
5792
}
5794
5793
5795
5794
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
@@ -5901,6 +5900,10 @@ namespace ts {
5901
5900
}
5902
5901
5903
5902
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
5903
+ const transformed = getTransformedIndexedAccessType(type);
5904
+ if (transformed) {
5905
+ return transformed;
5906
+ }
5904
5907
const baseObjectType = getBaseConstraintOfType(type.objectType);
5905
5908
const baseIndexType = getBaseConstraintOfType(type.indexType);
5906
5909
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
@@ -5972,11 +5975,18 @@ namespace ts {
5972
5975
return stringType;
5973
5976
}
5974
5977
if (t.flags & TypeFlags.IndexedAccess) {
5978
+ const transformed = getTransformedIndexedAccessType(<IndexedAccessType>t);
5979
+ if (transformed) {
5980
+ return getBaseConstraint(transformed);
5981
+ }
5975
5982
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
5976
5983
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
5977
5984
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
5978
5985
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
5979
5986
}
5987
+ if (isGenericMappedType(t)) {
5988
+ return emptyObjectType;
5989
+ }
5980
5990
return t;
5981
5991
}
5982
5992
}
@@ -7604,26 +7614,73 @@ namespace ts {
7604
7614
return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
7605
7615
}
7606
7616
7607
- function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
7608
- // If the index type is generic, if the object type is generic and doesn't originate in an expression,
7609
- // or if the object type is a mapped type with a generic constraint, we are performing a higher-order
7610
- // index access where we cannot meaningfully access the properties of the object type. Note that for a
7611
- // generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to
7612
- // preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
7613
- // eagerly using the constraint type of 'this' at the given location.
7614
- if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) ||
7615
- maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) ||
7616
- isGenericMappedType(objectType)) {
7617
+ function isGenericObjectType(type: Type): boolean {
7618
+ return type.flags & TypeFlags.TypeVariable ? true :
7619
+ getObjectFlags(type) & ObjectFlags.Mapped ? isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type)) :
7620
+ type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericObjectType) :
7621
+ false;
7622
+ }
7623
+
7624
+ function isGenericIndexType(type: Type): boolean {
7625
+ return type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) ? true :
7626
+ type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericIndexType) :
7627
+ false;
7628
+ }
7629
+
7630
+ // Return true if the given type is a non-generic object type with a string index signature and no
7631
+ // other members.
7632
+ function isStringIndexOnlyType(type: Type) {
7633
+ if (type.flags & TypeFlags.Object && !isGenericMappedType(type)) {
7634
+ const t = resolveStructuredTypeMembers(<ObjectType>type);
7635
+ return t.properties.length === 0 &&
7636
+ t.callSignatures.length === 0 && t.constructSignatures.length === 0 &&
7637
+ t.stringIndexInfo && !t.numberIndexInfo;
7638
+ }
7639
+ return false;
7640
+ }
7641
+
7642
+ // Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
7643
+ // more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
7644
+ // transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
7645
+ // access types with default property values as expressed by D.
7646
+ function getTransformedIndexedAccessType(type: IndexedAccessType): Type {
7647
+ const objectType = type.objectType;
7648
+ if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType) && some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
7649
+ const regularTypes: Type[] = [];
7650
+ const stringIndexTypes: Type[] = [];
7651
+ for (const t of (<IntersectionType>objectType).types) {
7652
+ if (isStringIndexOnlyType(t)) {
7653
+ stringIndexTypes.push(getIndexTypeOfType(t, IndexKind.String));
7654
+ }
7655
+ else {
7656
+ regularTypes.push(t);
7657
+ }
7658
+ }
7659
+ return getUnionType([
7660
+ getIndexedAccessType(getIntersectionType(regularTypes), type.indexType),
7661
+ getIntersectionType(stringIndexTypes)
7662
+ ]);
7663
+ }
7664
+ return undefined;
7665
+ }
7666
+
7667
+ function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
7668
+ // If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper
7669
+ // that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
7670
+ // construct the type Box<T[X]>.
7671
+ if (isGenericMappedType(objectType)) {
7672
+ return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7673
+ }
7674
+ // Otherwise, if the index type is generic, or if the object type is generic and doesn't originate in an
7675
+ // expression, we are performing a higher-order index access where we cannot meaningfully access the properties
7676
+ // of the object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates
7677
+ // in an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
7678
+ // has always been resolved eagerly using the constraint type of 'this' at the given location.
7679
+ if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) {
7617
7680
if (objectType.flags & TypeFlags.Any) {
7618
7681
return objectType;
7619
7682
}
7620
- // If the object type is a mapped type { [P in K]: E }, we instantiate E using a mapper that substitutes
7621
- // the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the
7622
- // type Box<T[X]>.
7623
- if (isGenericMappedType(objectType)) {
7624
- return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7625
- }
7626
- // Otherwise we defer the operation by creating an indexed access type.
7683
+ // Defer the operation by creating an indexed access type.
7627
7684
const id = objectType.id + "," + indexType.id;
7628
7685
let type = indexedAccessTypes.get(id);
7629
7686
if (!type) {
@@ -8017,7 +8074,7 @@ namespace ts {
8017
8074
8018
8075
function cloneTypeMapper(mapper: TypeMapper): TypeMapper {
8019
8076
return mapper && isInferenceContext(mapper) ?
8020
- createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) :
8077
+ createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper. inferences) :
8021
8078
mapper;
8022
8079
}
8023
8080
@@ -8458,7 +8515,7 @@ namespace ts {
8458
8515
ignoreReturnTypes: boolean,
8459
8516
reportErrors: boolean,
8460
8517
errorReporter: ErrorReporter,
8461
- compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary ): Ternary {
8518
+ compareTypes: TypeComparer ): Ternary {
8462
8519
// TODO (drosen): De-duplicate code between related functions.
8463
8520
if (source === target) {
8464
8521
return Ternary.True;
@@ -8468,7 +8525,7 @@ namespace ts {
8468
8525
}
8469
8526
8470
8527
if (source.typeParameters) {
8471
- source = instantiateSignatureInContextOf(source, target);
8528
+ source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes );
8472
8529
}
8473
8530
8474
8531
let result = Ternary.True;
@@ -9614,6 +9671,11 @@ namespace ts {
9614
9671
if (sourceInfo) {
9615
9672
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
9616
9673
}
9674
+ if (isGenericMappedType(source)) {
9675
+ // A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U }
9676
+ // if T is related to U.
9677
+ return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), targetInfo.type, reportErrors);
9678
+ }
9617
9679
if (isObjectLiteralType(source)) {
9618
9680
let related = Ternary.True;
9619
9681
if (kind === IndexKind.String) {
@@ -10216,13 +10278,14 @@ namespace ts {
10216
10278
}
10217
10279
}
10218
10280
10219
- function createInferenceContext(signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext {
10281
+ function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
10220
10282
const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo);
10221
10283
const context = mapper as InferenceContext;
10222
10284
context.mappedTypes = signature.typeParameters;
10223
10285
context.signature = signature;
10224
10286
context.inferences = inferences;
10225
10287
context.flags = flags;
10288
+ context.compareTypes = compareTypes || compareTypesAssignable;
10226
10289
return context;
10227
10290
10228
10291
function mapper(t: Type): Type {
@@ -10325,6 +10388,19 @@ namespace ts {
10325
10388
}
10326
10389
}
10327
10390
10391
+ function isPossiblyAssignableTo(source: Type, target: Type) {
10392
+ const properties = getPropertiesOfObjectType(target);
10393
+ for (const targetProp of properties) {
10394
+ if (!(targetProp.flags & (SymbolFlags.Optional | SymbolFlags.Prototype))) {
10395
+ const sourceProp = getPropertyOfObjectType(source, targetProp.escapedName);
10396
+ if (!sourceProp) {
10397
+ return false;
10398
+ }
10399
+ }
10400
+ }
10401
+ return true;
10402
+ }
10403
+
10328
10404
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
10329
10405
let symbolStack: Symbol[];
10330
10406
let visited: Map<boolean>;
@@ -10518,10 +10594,14 @@ namespace ts {
10518
10594
return;
10519
10595
}
10520
10596
}
10521
- inferFromProperties(source, target);
10522
- inferFromSignatures(source, target, SignatureKind.Call);
10523
- inferFromSignatures(source, target, SignatureKind.Construct);
10524
- inferFromIndexTypes(source, target);
10597
+ // Infer from the members of source and target only if the two types are possibly related. We check
10598
+ // in both directions because we may be inferring for a co-variant or a contra-variant position.
10599
+ if (isPossiblyAssignableTo(source, target) || isPossiblyAssignableTo(target, source)) {
10600
+ inferFromProperties(source, target);
10601
+ inferFromSignatures(source, target, SignatureKind.Call);
10602
+ inferFromSignatures(source, target, SignatureKind.Construct);
10603
+ inferFromIndexTypes(source, target);
10604
+ }
10525
10605
}
10526
10606
10527
10607
function inferFromProperties(source: Type, target: Type) {
@@ -10653,7 +10733,7 @@ namespace ts {
10653
10733
const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]);
10654
10734
if (constraint) {
10655
10735
const instantiatedConstraint = instantiateType(constraint, context);
10656
- if (!isTypeAssignableTo (inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
10736
+ if (!context.compareTypes (inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
10657
10737
inference.inferredType = inferredType = instantiatedConstraint;
10658
10738
}
10659
10739
}
@@ -15054,8 +15134,8 @@ namespace ts {
15054
15134
}
15055
15135
15056
15136
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
15057
- function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper): Signature {
15058
- const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes);
15137
+ function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer ): Signature {
15138
+ const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes );
15059
15139
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
15060
15140
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
15061
15141
inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target);
0 commit comments