@@ -7612,22 +7612,6 @@ namespace ts {
7612
7612
return anyType;
7613
7613
}
7614
7614
7615
- function getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
7616
- if (accessNode) {
7617
- // Check if the index type is assignable to 'keyof T' for the object type.
7618
- if (!isTypeAssignableTo(indexType, getIndexType(type))) {
7619
- error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(type));
7620
- return unknownType;
7621
- }
7622
- if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && type.declaration.readonlyToken) {
7623
- error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
7624
- }
7625
- }
7626
- const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]);
7627
- const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
7628
- return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
7629
- }
7630
-
7631
7615
function isGenericObjectType(type: Type): boolean {
7632
7616
return type.flags & TypeFlags.TypeVariable ? true :
7633
7617
getObjectFlags(type) & ObjectFlags.Mapped ? isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type)) :
@@ -7653,12 +7637,14 @@ namespace ts {
7653
7637
return false;
7654
7638
}
7655
7639
7656
- // Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
7657
- // more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
7658
- // transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
7659
- // access types with default property values as expressed by D.
7640
+ // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
7641
+ // undefined if no transformation is possible.
7660
7642
function getTransformedIndexedAccessType(type: IndexedAccessType): Type {
7661
7643
const objectType = type.objectType;
7644
+ // Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
7645
+ // more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
7646
+ // transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
7647
+ // access types with default property values as expressed by D.
7662
7648
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType) && some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
7663
7649
const regularTypes: Type[] = [];
7664
7650
const stringIndexTypes: Type[] = [];
@@ -7675,20 +7661,23 @@ namespace ts {
7675
7661
getIntersectionType(stringIndexTypes)
7676
7662
]);
7677
7663
}
7678
- return undefined;
7679
- }
7680
-
7681
- function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
7682
- // If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper
7664
+ // If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper
7683
7665
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
7684
7666
// construct the type Box<T[X]>.
7685
7667
if (isGenericMappedType(objectType)) {
7686
- return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7668
+ const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>objectType)], [type.indexType]);
7669
+ const objectTypeMapper = (<MappedType>objectType).mapper;
7670
+ const templateMapper = objectTypeMapper ? combineTypeMappers(objectTypeMapper, mapper) : mapper;
7671
+ return instantiateType(getTemplateTypeFromMappedType(<MappedType>objectType), templateMapper);
7687
7672
}
7688
- // Otherwise, if the index type is generic, or if the object type is generic and doesn't originate in an
7689
- // expression, we are performing a higher-order index access where we cannot meaningfully access the properties
7690
- // of the object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates
7691
- // in an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
7673
+ return undefined;
7674
+ }
7675
+
7676
+ function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
7677
+ // If the index type is generic, or if the object type is generic and doesn't originate in an expression,
7678
+ // we are performing a higher-order index access where we cannot meaningfully access the properties of the
7679
+ // object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates in
7680
+ // an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
7692
7681
// has always been resolved eagerly using the constraint type of 'this' at the given location.
7693
7682
if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) {
7694
7683
if (objectType.flags & TypeFlags.Any) {
@@ -9310,7 +9299,7 @@ namespace ts {
9310
9299
else if (target.flags & TypeFlags.IndexedAccess) {
9311
9300
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
9312
9301
// A is the apparent type of S.
9313
- const constraint = getConstraintOfType (<IndexedAccessType>target);
9302
+ const constraint = getConstraintOfIndexedAccess (<IndexedAccessType>target);
9314
9303
if (constraint) {
9315
9304
if (result = isRelatedTo(source, constraint, reportErrors)) {
9316
9305
errorInfo = saveErrorInfo;
@@ -9350,7 +9339,7 @@ namespace ts {
9350
9339
else if (source.flags & TypeFlags.IndexedAccess) {
9351
9340
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
9352
9341
// A is the apparent type of S.
9353
- const constraint = getConstraintOfType (<IndexedAccessType>source);
9342
+ const constraint = getConstraintOfIndexedAccess (<IndexedAccessType>source);
9354
9343
if (constraint) {
9355
9344
if (result = isRelatedTo(constraint, target, reportErrors)) {
9356
9345
errorInfo = saveErrorInfo;
@@ -18839,6 +18828,10 @@ namespace ts {
18839
18828
const objectType = (<IndexedAccessType>type).objectType;
18840
18829
const indexType = (<IndexedAccessType>type).indexType;
18841
18830
if (isTypeAssignableTo(indexType, getIndexType(objectType))) {
18831
+ if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
18832
+ getObjectFlags(objectType) & ObjectFlags.Mapped && (<MappedType>objectType).declaration.readonlyToken) {
18833
+ error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
18834
+ }
18842
18835
return type;
18843
18836
}
18844
18837
// Check if we're indexing with a numeric type and if either object or index types
0 commit comments