@@ -7188,34 +7188,37 @@ namespace ts {
7188
7188
// Return the lower bound of the key type in a mapped type. Intuitively, the lower
7189
7189
// bound includes those keys that are known to always be present, for example because
7190
7190
// because of constraints on type parameters (e.g. 'keyof T' for a constrained T).
7191
- function getLowerBoundOfKeyType(type: Type): Type {
7191
+ // The isIndexType flag indicates that the type is the index type of an indexed
7192
+ // access that is the target of an assignment.
7193
+ function getLowerBoundOfKeyType(type: Type, isIndexType: boolean): Type {
7192
7194
if (type.flags & (TypeFlags.Any | TypeFlags.Primitive)) {
7193
7195
return type;
7194
7196
}
7195
7197
if (type.flags & TypeFlags.Index) {
7196
- return getIndexType(getApparentType((<IndexType>type).type));
7198
+ const keys = getIndexType(getApparentType((<IndexType>type).type));
7199
+ return isIndexType ? filterType(keys, t => !!(t.flags & TypeFlags.Literal)) : keys;
7197
7200
}
7198
7201
if (type.flags & TypeFlags.Conditional) {
7199
- return getLowerBoundOfConditionalType(<ConditionalType>type);
7202
+ if ((<ConditionalType>type).root.isDistributive) {
7203
+ const checkType = (<ConditionalType>type).checkType;
7204
+ const constraint = getLowerBoundOfKeyType(checkType, isIndexType);
7205
+ if (constraint !== checkType) {
7206
+ const mapper = makeUnaryTypeMapper((<ConditionalType>type).root.checkType, constraint);
7207
+ return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers(mapper, (<ConditionalType>type).mapper));
7208
+ }
7209
+ }
7210
+ return type;
7200
7211
}
7201
7212
if (type.flags & TypeFlags.Union) {
7202
- return getUnionType(sameMap((<UnionType>type).types, getLowerBoundOfKeyType));
7213
+ return getUnionType(sameMap((<UnionType>type).types, t => getLowerBoundOfKeyType(t, isIndexType) ));
7203
7214
}
7204
7215
if (type.flags & TypeFlags.Intersection) {
7205
- return getIntersectionType(sameMap((<UnionType>type).types, getLowerBoundOfKeyType));
7216
+ return getIntersectionType(sameMap((<UnionType>type).types, t => getLowerBoundOfKeyType(t, isIndexType) ));
7206
7217
}
7207
- return neverType;
7208
- }
7209
-
7210
- function getLowerBoundOfConditionalType(type: ConditionalType) {
7211
- if (type.root.isDistributive) {
7212
- const constraint = getLowerBoundOfKeyType(type.checkType);
7213
- if (constraint !== type.checkType) {
7214
- const mapper = makeUnaryTypeMapper(type.root.checkType, constraint);
7215
- return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper));
7216
- }
7218
+ if (isIndexType && type.flags & TypeFlags.Instantiable) {
7219
+ return getLowerBoundOfKeyType(getConstraintOfType(type) || neverType, isIndexType);
7217
7220
}
7218
- return type ;
7221
+ return neverType ;
7219
7222
}
7220
7223
7221
7224
/** Resolve the members of a mapped type { [P in K]: T } */
@@ -7246,7 +7249,7 @@ namespace ts {
7246
7249
}
7247
7250
}
7248
7251
else {
7249
- forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType);
7252
+ forEachType(getLowerBoundOfKeyType(constraintType, /*isIndexType*/ false ), addMemberForKeyType);
7250
7253
}
7251
7254
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
7252
7255
@@ -7523,7 +7526,7 @@ namespace ts {
7523
7526
// a union - once negated types exist and are applied to the conditional false branch, this "constraint"
7524
7527
// likely doesn't need to exist.
7525
7528
if (type.root.isDistributive && type.restrictiveInstantiation !== type) {
7526
- const simplified = getSimplifiedType(type.checkType);
7529
+ const simplified = getSimplifiedType(type.checkType, /*writing*/ false );
7527
7530
const constraint = simplified === type.checkType ? getConstraintOfType(simplified) : simplified;
7528
7531
if (constraint && constraint !== type.checkType) {
7529
7532
const mapper = makeUnaryTypeMapper(type.root.checkType, constraint);
@@ -7630,7 +7633,7 @@ namespace ts {
7630
7633
return t.immediateBaseConstraint = noConstraintType;
7631
7634
}
7632
7635
constraintDepth++;
7633
- let result = computeBaseConstraint(getSimplifiedType(t));
7636
+ let result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false ));
7634
7637
constraintDepth--;
7635
7638
if (!popTypeResolution()) {
7636
7639
if (t.flags & TypeFlags.TypeParameter) {
@@ -10003,53 +10006,68 @@ namespace ts {
10003
10006
return maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive | TypeFlags.Index);
10004
10007
}
10005
10008
10006
- function getSimplifiedType(type: Type): Type {
10007
- return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(<IndexedAccessType>type) : type;
10009
+ function getSimplifiedType(type: Type, writing: boolean ): Type {
10010
+ return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(<IndexedAccessType>type, writing ) : type;
10008
10011
}
10009
10012
10010
- function distributeIndexOverObjectType(objectType: Type, indexType: Type) {
10011
- // (T | U)[K] -> T[K] | U[K]
10012
- if (objectType.flags & TypeFlags.Union) {
10013
- return mapType(objectType, t => getSimplifiedType(getIndexedAccessType(t, indexType)));
10014
- }
10013
+ function distributeIndexOverObjectType(objectType: Type, indexType: Type, writing: boolean) {
10014
+ // (T | U)[K] -> T[K] | U[K] (reading)
10015
+ // (T | U)[K] -> T[K] & U[K] (writing)
10015
10016
// (T & U)[K] -> T[K] & U[K]
10016
- if (objectType.flags & TypeFlags.Intersection) {
10017
- return getIntersectionType(map((objectType as IntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))));
10017
+ if (objectType.flags & TypeFlags.UnionOrIntersection) {
10018
+ const types = map((objectType as UnionOrIntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType), writing));
10019
+ return objectType.flags & TypeFlags.Intersection || writing ? getIntersectionType(types) : getUnionType(types);
10018
10020
}
10019
10021
}
10020
10022
10021
- // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
10022
- // the type itself if no transformation is possible.
10023
- function getSimplifiedIndexedAccessType(type: IndexedAccessType): Type {
10024
- if (type.simplified) {
10025
- return type.simplified === circularConstraintType ? type : type.simplified;
10023
+ function distributeObjectOverIndexType(objectType: Type, indexType: Type, writing: boolean) {
10024
+ // T[A | B] -> T[A] | T[B] (reading)
10025
+ // T[A | B] -> T[A] & T[B] (writing)
10026
+ if (indexType.flags & TypeFlags.Union) {
10027
+ const types = map((indexType as UnionType).types, t => getSimplifiedType(getIndexedAccessType(objectType, t), writing));
10028
+ return writing ? getIntersectionType(types) : getUnionType(types);
10026
10029
}
10027
- type.simplified = circularConstraintType;
10030
+ }
10031
+
10032
+ // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
10033
+ // the type itself if no transformation is possible. The writing flag indicates that the type is
10034
+ // the target of an assignment.
10035
+ function getSimplifiedIndexedAccessType(type: IndexedAccessType, writing: boolean): Type {
10036
+ const cache = writing ? "simplifiedForWriting" : "simplifiedForReading";
10037
+ if (type[cache]) {
10038
+ return type[cache] === circularConstraintType ? type : type[cache]!;
10039
+ }
10040
+ type[cache] = circularConstraintType;
10028
10041
// We recursively simplify the object type as it may in turn be an indexed access type. For example, with
10029
10042
// '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type.
10030
- const objectType = getSimplifiedType(type.objectType);
10031
- const indexType = getSimplifiedType(type.indexType);
10032
- // T[A | B] -> T[A] | T[B]
10033
- if (indexType.flags & TypeFlags.Union) {
10034
- return type.simplified = mapType(indexType, t => getSimplifiedType(getIndexedAccessType(objectType, t)));
10043
+ const objectType = getSimplifiedType(type.objectType, writing);
10044
+ const indexType = getSimplifiedType(type.indexType, writing);
10045
+ // T[A | B] -> T[A] | T[B] (reading)
10046
+ // T[A | B] -> T[A] & T[B] (writing)
10047
+ const distributedOverIndex = distributeObjectOverIndexType(objectType, indexType, writing);
10048
+ if (distributedOverIndex) {
10049
+ return type[cache] = distributedOverIndex;
10035
10050
}
10036
10051
// Only do the inner distributions if the index can no longer be instantiated to cause index distribution again
10037
10052
if (!(indexType.flags & TypeFlags.Instantiable)) {
10038
- const simplified = distributeIndexOverObjectType(objectType, indexType);
10039
- if (simplified) {
10040
- return type.simplified = simplified;
10053
+ // (T | U)[K] -> T[K] | U[K] (reading)
10054
+ // (T | U)[K] -> T[K] & U[K] (writing)
10055
+ // (T & U)[K] -> T[K] & U[K]
10056
+ const distributedOverObject = distributeIndexOverObjectType(objectType, indexType, writing);
10057
+ if (distributedOverObject) {
10058
+ return type[cache] = distributedOverObject;
10041
10059
}
10042
10060
}
10043
- // So ultimately:
10061
+ // So ultimately (reading) :
10044
10062
// ((A & B) | C)[K1 | K2] -> ((A & B) | C)[K1] | ((A & B) | C)[K2] -> (A & B)[K1] | C[K1] | (A & B)[K2] | C[K2] -> (A[K1] & B[K1]) | C[K1] | (A[K2] & B[K2]) | C[K2]
10045
10063
10046
10064
// If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper
10047
10065
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
10048
10066
// construct the type Box<T[X]>.
10049
10067
if (isGenericMappedType(objectType)) {
10050
- return type.simplified = mapType(substituteIndexedMappedType(objectType, type.indexType), getSimplifiedType);
10068
+ return type[cache] = mapType(substituteIndexedMappedType(objectType, type.indexType), t => getSimplifiedType(t, writing) );
10051
10069
}
10052
- return type.simplified = type;
10070
+ return type[cache] = type;
10053
10071
}
10054
10072
10055
10073
function substituteIndexedMappedType(objectType: MappedType, index: Type) {
@@ -12197,10 +12215,10 @@ namespace ts {
12197
12215
target = (<SubstitutionType>target).typeVariable;
12198
12216
}
12199
12217
if (source.flags & TypeFlags.IndexedAccess) {
12200
- source = getSimplifiedType(source);
12218
+ source = getSimplifiedType(source, /*writing*/ false );
12201
12219
}
12202
12220
if (target.flags & TypeFlags.IndexedAccess) {
12203
- target = getSimplifiedType(target);
12221
+ target = getSimplifiedType(target, /*writing*/ true );
12204
12222
}
12205
12223
12206
12224
// Try to see if we're relating something like `Foo` -> `Bar | null | undefined`.
@@ -12780,7 +12798,7 @@ namespace ts {
12780
12798
}
12781
12799
// A type S is assignable to keyof T if S is assignable to keyof C, where C is the
12782
12800
// simplified form of T or, if T doesn't simplify, the constraint of T.
12783
- const simplified = getSimplifiedType((<IndexType>target).type);
12801
+ const simplified = getSimplifiedType((<IndexType>target).type, /*writing*/ false );
12784
12802
const constraint = simplified !== (<IndexType>target).type ? simplified : getConstraintOfType((<IndexType>target).type);
12785
12803
if (constraint) {
12786
12804
// We require Ternary.True here such that circular constraints don't cause
@@ -12795,12 +12813,26 @@ namespace ts {
12795
12813
else if (target.flags & TypeFlags.IndexedAccess) {
12796
12814
// A type S is related to a type T[K], where T and K aren't both type variables, if S is related to C,
12797
12815
// where C is the base constraint of T[K]
12798
- if (relation !== identityRelation &&
12799
- !(isGenericObjectType((<IndexedAccessType>target).objectType) && isGenericIndexType((<IndexedAccessType>target).indexType))) {
12800
- const constraint = getBaseConstraintOfType(target);
12801
- if (constraint && constraint !== target) {
12802
- if (result = isRelatedTo(source, constraint, reportErrors)) {
12803
- return result;
12816
+ if (relation !== identityRelation) {
12817
+ const objectType = (<IndexedAccessType>target).objectType
12818
+ const indexType = (<IndexedAccessType>target).indexType;
12819
+ if (indexType.flags & TypeFlags.StructuredOrInstantiable) {
12820
+ const keyType = getLowerBoundOfKeyType(indexType, /*isIndexType*/ true);
12821
+ if (keyType !== indexType && !(keyType.flags & TypeFlags.Never)) {
12822
+ const targetType = keyType.flags & TypeFlags.Union ?
12823
+ getIntersectionType(map((<UnionType>keyType).types, t => getIndexedAccessType(objectType, t))) :
12824
+ getIndexedAccessType(objectType, keyType);
12825
+ if (result = isRelatedTo(source, targetType, reportErrors)) {
12826
+ return result;
12827
+ }
12828
+ }
12829
+ }
12830
+ else {
12831
+ const constraint = getConstraintOfType(objectType);
12832
+ if (constraint) {
12833
+ if (result = isRelatedTo(source, getIndexedAccessType(constraint, indexType), reportErrors)) {
12834
+ return result;
12835
+ }
12804
12836
}
12805
12837
}
12806
12838
}
@@ -14660,16 +14692,16 @@ namespace ts {
14660
14692
}
14661
14693
else {
14662
14694
// Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
14663
- const simplified = getSimplifiedType(target);
14695
+ const simplified = getSimplifiedType(target, /*writing*/ false );
14664
14696
if (simplified !== target) {
14665
14697
inferFromTypesOnce(source, simplified);
14666
14698
}
14667
14699
else if (target.flags & TypeFlags.IndexedAccess) {
14668
- const indexType = getSimplifiedType((target as IndexedAccessType).indexType);
14700
+ const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false );
14669
14701
// Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider
14670
14702
// that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can.
14671
14703
if (indexType.flags & TypeFlags.Instantiable) {
14672
- const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType), indexType);
14704
+ const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false ), indexType, /*writing*/ false );
14673
14705
if (simplified && simplified !== target) {
14674
14706
inferFromTypesOnce(source, simplified);
14675
14707
}
@@ -15697,24 +15729,19 @@ namespace ts {
15697
15729
if (!(type.flags & TypeFlags.Union)) {
15698
15730
return mapper(type);
15699
15731
}
15700
- const types = (<UnionType>type).types;
15701
- let mappedType: Type | undefined;
15702
15732
let mappedTypes: Type[] | undefined;
15703
- for (const current of types) {
15704
- const t = mapper(current);
15705
- if (t) {
15706
- if (!mappedType) {
15707
- mappedType = t;
15708
- }
15709
- else if (!mappedTypes) {
15710
- mappedTypes = [mappedType, t];
15733
+ for (const t of (<UnionType>type).types) {
15734
+ const mapped = mapper(t);
15735
+ if (mapped) {
15736
+ if (!mappedTypes) {
15737
+ mappedTypes = [mapped];
15711
15738
}
15712
15739
else {
15713
- mappedTypes.push(t );
15740
+ mappedTypes.push(mapped );
15714
15741
}
15715
15742
}
15716
15743
}
15717
- return mappedTypes ? getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal) : mappedType ;
15744
+ return mappedTypes && getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal);
15718
15745
}
15719
15746
15720
15747
function extractTypesOfKind(type: Type, kind: TypeFlags) {
0 commit comments