Skip to content

Commit caea4f3

Browse files
committed
Properly handle constraints for types like (T & { [x: string]: D })[K]
1 parent b2ba275 commit caea4f3

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

src/compiler/checker.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5900,6 +5900,10 @@ namespace ts {
59005900
}
59015901

59025902
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
5903+
const transformed = getTransformedIndexedAccessType(type);
5904+
if (transformed) {
5905+
return transformed;
5906+
}
59035907
const baseObjectType = getBaseConstraintOfType(type.objectType);
59045908
const baseIndexType = getBaseConstraintOfType(type.indexType);
59055909
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
@@ -5971,11 +5975,18 @@ namespace ts {
59715975
return stringType;
59725976
}
59735977
if (t.flags & TypeFlags.IndexedAccess) {
5978+
const transformed = getTransformedIndexedAccessType(<IndexedAccessType>t);
5979+
if (transformed) {
5980+
return getBaseConstraint(transformed);
5981+
}
59745982
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
59755983
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
59765984
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
59775985
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
59785986
}
5987+
if (isGenericMappedType(t)) {
5988+
return emptyObjectType;
5989+
}
59795990
return t;
59805991
}
59815992
}
@@ -7610,7 +7621,44 @@ namespace ts {
76107621
false;
76117622
}
76127623

7613-
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
7624+
// Return true if the given type is a non-generic object type with a string index signature and no
7625+
// other members.
7626+
function isStringIndexOnlyType(type: Type) {
7627+
if (type.flags & TypeFlags.Object && !isGenericMappedType(type)) {
7628+
const t = resolveStructuredTypeMembers(<ObjectType>type);
7629+
return t.properties.length === 0 &&
7630+
t.callSignatures.length === 0 && t.constructSignatures.length === 0 &&
7631+
t.stringIndexInfo && !t.numberIndexInfo;
7632+
}
7633+
return false;
7634+
}
7635+
7636+
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
7637+
// more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
7638+
// transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
7639+
// access types with default property values as expressed by D.
7640+
function getTransformedIndexedAccessType(type: IndexedAccessType): Type {
7641+
const objectType = type.objectType;
7642+
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType) && some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
7643+
const regularTypes: Type[] = [];
7644+
const stringIndexTypes: Type[] = [];
7645+
for (const t of (<IntersectionType>objectType).types) {
7646+
if (isStringIndexOnlyType(t)) {
7647+
stringIndexTypes.push(getIndexTypeOfType(t, IndexKind.String));
7648+
}
7649+
else {
7650+
regularTypes.push(t);
7651+
}
7652+
}
7653+
return getUnionType([
7654+
getIndexedAccessType(getIntersectionType(regularTypes), type.indexType),
7655+
getIntersectionType(stringIndexTypes)
7656+
]);
7657+
}
7658+
return undefined;
7659+
}
7660+
7661+
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
76147662
// If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper
76157663
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
76167664
// construct the type Box<T[X]>.
@@ -18662,6 +18710,8 @@ namespace ts {
1866218710
}
1866318711

1866418712
function checkIndexedAccessType(node: IndexedAccessTypeNode) {
18713+
checkSourceElement(node.objectType);
18714+
checkSourceElement(node.indexType);
1866518715
checkIndexedAccessIndexType(getTypeFromIndexedAccessTypeNode(node), node);
1866618716
}
1866718717

0 commit comments

Comments
 (0)