@@ -5906,10 +5906,6 @@ namespace ts {
5906
5906
}
5907
5907
5908
5908
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
5909
- const transformed = getTransformedIndexedAccessType(type);
5910
- if (transformed) {
5911
- return transformed;
5912
- }
5913
5909
const baseObjectType = getBaseConstraintOfType(type.objectType);
5914
5910
const baseIndexType = getBaseConstraintOfType(type.indexType);
5915
5911
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
@@ -7596,22 +7592,6 @@ namespace ts {
7596
7592
return anyType;
7597
7593
}
7598
7594
7599
- function getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
7600
- if (accessNode) {
7601
- // Check if the index type is assignable to 'keyof T' for the object type.
7602
- if (!isTypeAssignableTo(indexType, getIndexType(type))) {
7603
- error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(type));
7604
- return unknownType;
7605
- }
7606
- if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && type.declaration.readonlyToken) {
7607
- error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
7608
- }
7609
- }
7610
- const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]);
7611
- const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
7612
- return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
7613
- }
7614
-
7615
7595
function isGenericObjectType(type: Type): boolean {
7616
7596
return type.flags & TypeFlags.TypeVariable ? true :
7617
7597
getObjectFlags(type) & ObjectFlags.Mapped ? isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type)) :
@@ -7637,12 +7617,14 @@ namespace ts {
7637
7617
return false;
7638
7618
}
7639
7619
7640
- // Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
7641
- // more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
7642
- // transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
7643
- // access types with default property values as expressed by D.
7620
+ // Transform an indexed access occurring in a read position to a simpler form. Return the simpler form,
7621
+ // or undefined if no transformation is possible.
7644
7622
function getTransformedIndexedAccessType(type: IndexedAccessType): Type {
7645
7623
const objectType = type.objectType;
7624
+ // Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
7625
+ // more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
7626
+ // transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
7627
+ // access types with default property values as expressed by D.
7646
7628
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType) && some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
7647
7629
const regularTypes: Type[] = [];
7648
7630
const stringIndexTypes: Type[] = [];
@@ -7659,20 +7641,23 @@ namespace ts {
7659
7641
getIntersectionType(stringIndexTypes)
7660
7642
]);
7661
7643
}
7662
- return undefined;
7663
- }
7664
-
7665
- function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
7666
- // If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper
7644
+ // If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper
7667
7645
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
7668
7646
// construct the type Box<T[X]>.
7669
7647
if (isGenericMappedType(objectType)) {
7670
- return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7648
+ const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>objectType)], [type.indexType]);
7649
+ const objectTypeMapper = (<MappedType>objectType).mapper;
7650
+ const templateMapper = objectTypeMapper ? combineTypeMappers(objectTypeMapper, mapper) : mapper;
7651
+ return instantiateType(getTemplateTypeFromMappedType(<MappedType>objectType), templateMapper);
7671
7652
}
7672
- // Otherwise, if the index type is generic, or if the object type is generic and doesn't originate in an
7673
- // expression, we are performing a higher-order index access where we cannot meaningfully access the properties
7674
- // of the object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates
7675
- // in an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
7653
+ return undefined;
7654
+ }
7655
+
7656
+ function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
7657
+ // If the index type is generic, or if the object type is generic and doesn't originate in an expression,
7658
+ // we are performing a higher-order index access where we cannot meaningfully access the properties of the
7659
+ // object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates in
7660
+ // an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
7676
7661
// has always been resolved eagerly using the constraint type of 'this' at the given location.
7677
7662
if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) {
7678
7663
if (objectType.flags & TypeFlags.Any) {
@@ -9334,7 +9319,7 @@ namespace ts {
9334
9319
else if (source.flags & TypeFlags.IndexedAccess) {
9335
9320
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
9336
9321
// A is the apparent type of S.
9337
- const constraint = getConstraintOfType (<IndexedAccessType>source);
9322
+ const constraint = getTransformedIndexedAccessType(<IndexedAccessType>source) || getConstraintOfIndexedAccess (<IndexedAccessType>source);
9338
9323
if (constraint) {
9339
9324
if (result = isRelatedTo(constraint, target, reportErrors)) {
9340
9325
errorInfo = saveErrorInfo;
@@ -18810,6 +18795,10 @@ namespace ts {
18810
18795
const objectType = (<IndexedAccessType>type).objectType;
18811
18796
const indexType = (<IndexedAccessType>type).indexType;
18812
18797
if (isTypeAssignableTo(indexType, getIndexType(objectType))) {
18798
+ if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
18799
+ getObjectFlags(objectType) & ObjectFlags.Mapped && (<MappedType>objectType).declaration.readonlyToken) {
18800
+ error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
18801
+ }
18813
18802
return type;
18814
18803
}
18815
18804
// Check if we're indexing with a numeric type and if either object or index types
0 commit comments