Skip to content

Commit 5895057

Browse files
committed
Defer indexed access type resolution in more cases
1 parent f945b26 commit 5895057

File tree

1 file changed

+32
-27
lines changed

1 file changed

+32
-27
lines changed

src/compiler/checker.ts

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2583,10 +2583,8 @@ namespace ts {
25832583
}
25842584

25852585
function createTypeNodeFromObjectType(type: ObjectType): TypeNode {
2586-
if (type.objectFlags & ObjectFlags.Mapped) {
2587-
if (getConstraintTypeFromMappedType(<MappedType>type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
2588-
return createMappedTypeNodeFromType(<MappedType>type);
2589-
}
2586+
if (isGenericMappedType(type)) {
2587+
return createMappedTypeNodeFromType(<MappedType>type);
25902588
}
25912589

25922590
const resolved = resolveStructuredTypeMembers(type);
@@ -3489,11 +3487,9 @@ namespace ts {
34893487
}
34903488

34913489
function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) {
3492-
if (type.objectFlags & ObjectFlags.Mapped) {
3493-
if (getConstraintTypeFromMappedType(<MappedType>type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
3494-
writeMappedType(<MappedType>type);
3495-
return;
3496-
}
3490+
if (isGenericMappedType(type)) {
3491+
writeMappedType(<MappedType>type);
3492+
return;
34973493
}
34983494

34993495
const resolved = resolveStructuredTypeMembers(type);
@@ -5792,8 +5788,7 @@ namespace ts {
57925788
}
57935789

57945790
function isGenericMappedType(type: Type) {
5795-
return getObjectFlags(type) & ObjectFlags.Mapped &&
5796-
maybeTypeOfKind(getConstraintTypeFromMappedType(<MappedType>type), TypeFlags.TypeVariable | TypeFlags.Index);
5791+
return getObjectFlags(type) & ObjectFlags.Mapped && isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type));
57975792
}
57985793

57995794
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
@@ -7602,26 +7597,36 @@ namespace ts {
76027597
return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
76037598
}
76047599

7600+
function isGenericObjectType(type: Type): boolean {
7601+
return type.flags & TypeFlags.TypeVariable ? true :
7602+
getObjectFlags(type) & ObjectFlags.Mapped ? isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type)) :
7603+
type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericObjectType) :
7604+
false;
7605+
}
7606+
7607+
function isGenericIndexType(type: Type): boolean {
7608+
return type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) ? true :
7609+
type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericIndexType) :
7610+
false;
7611+
}
7612+
76057613
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
7606-
// If the index type is generic, if the object type is generic and doesn't originate in an expression,
7607-
// or if the object type is a mapped type with a generic constraint, we are performing a higher-order
7608-
// index access where we cannot meaningfully access the properties of the object type. Note that for a
7609-
// generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to
7610-
// preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
7611-
// eagerly using the constraint type of 'this' at the given location.
7612-
if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) ||
7613-
maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) ||
7614-
isGenericMappedType(objectType)) {
7614+
// If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper
7615+
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
7616+
// construct the type Box<T[X]>.
7617+
if (isGenericMappedType(objectType)) {
7618+
return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7619+
}
7620+
// Otherwise, if the index type is generic, or if the object type is generic and doesn't originate in an
7621+
// expression, we are performing a higher-order index access where we cannot meaningfully access the properties
7622+
// of the object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates
7623+
// in an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
7624+
// has always been resolved eagerly using the constraint type of 'this' at the given location.
7625+
if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) {
76157626
if (objectType.flags & TypeFlags.Any) {
76167627
return objectType;
76177628
}
7618-
// If the object type is a mapped type { [P in K]: E }, we instantiate E using a mapper that substitutes
7619-
// the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the
7620-
// type Box<T[X]>.
7621-
if (isGenericMappedType(objectType)) {
7622-
return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7623-
}
7624-
// Otherwise we defer the operation by creating an indexed access type.
7629+
// Defer the operation by creating an indexed access type.
76257630
const id = objectType.id + "," + indexType.id;
76267631
let type = indexedAccessTypes.get(id);
76277632
if (!type) {

0 commit comments

Comments
 (0)