Skip to content

Commit 4672457

Browse files
authored
Merge pull request #17455 from Microsoft/mappedTypeFixes
Mapped and indexed access type fixes
2 parents bb34bce + 9e90094 commit 4672457

File tree

6 files changed

+131
-15
lines changed

6 files changed

+131
-15
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 13 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);
@@ -7592,10 +7588,16 @@ namespace ts {
75927588
}
75937589

75947590
function getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
7595-
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
7596-
if (accessExpression && isAssignmentTarget(accessExpression) && type.declaration.readonlyToken) {
7597-
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
7598-
return unknownType;
7591+
if (accessNode) {
7592+
// Check if the index type is assignable to 'keyof T' for the object type.
7593+
if (!isTypeAssignableTo(indexType, getIndexType(type))) {
7594+
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(type));
7595+
return unknownType;
7596+
}
7597+
if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && type.declaration.readonlyToken) {
7598+
error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
7599+
return unknownType;
7600+
}
75997601
}
76007602
const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]);
76017603
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
@@ -18657,6 +18659,8 @@ namespace ts {
1865718659
}
1865818660

1865918661
function checkIndexedAccessType(node: IndexedAccessTypeNode) {
18662+
checkSourceElement(node.objectType);
18663+
checkSourceElement(node.indexType);
1866018664
checkIndexedAccessIndexType(getTypeFromIndexedAccessTypeNode(node), node);
1866118665
}
1866218666

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
tests/cases/compiler/anyIndexedAccessArrayNoException.ts(1,12): error TS1122: A tuple type element list cannot be empty.
12
tests/cases/compiler/anyIndexedAccessArrayNoException.ts(1,12): error TS2538: Type '[]' cannot be used as an index type.
23

34

4-
==== tests/cases/compiler/anyIndexedAccessArrayNoException.ts (1 errors) ====
5+
==== tests/cases/compiler/anyIndexedAccessArrayNoException.ts (2 errors) ====
56
var x: any[[]];
67
~~
8+
!!! error TS1122: A tuple type element list cannot be empty.
9+
~~
710
!!! error TS2538: Type '[]' cannot be used as an index type.
811

tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(35,21): error
1515
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(36,21): error TS2538: Type 'boolean' cannot be used as an index type.
1616
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(41,31): error TS2538: Type 'boolean' cannot be used as an index type.
1717
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(46,16): error TS2538: Type 'boolean' cannot be used as an index type.
18+
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(49,12): error TS1122: A tuple type element list cannot be empty.
1819
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(63,33): error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
1920
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(64,33): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
2021
Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
@@ -28,7 +29,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(76,5): error
2829
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error TS2322: Type 'keyof (T & U)' is not assignable to type 'keyof (T | U)'.
2930

3031

31-
==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (24 errors) ====
32+
==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (25 errors) ====
3233
class Shape {
3334
name: string;
3435
width: number;
@@ -112,6 +113,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error
112113

113114
type T60 = {}["toString"];
114115
type T61 = []["toString"];
116+
~~
117+
!!! error TS1122: A tuple type element list cannot be empty.
115118

116119
declare let cond: boolean;
117120

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(9,30): error TS2536: Type 'K' cannot be used to index type 'T1<K>'.
2+
tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(13,30): error TS2536: Type 'K' cannot be used to index type 'T3'.
3+
tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(15,47): error TS2536: Type 'S' cannot be used to index type 'AB'.
4+
tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(17,49): error TS2536: Type 'L' cannot be used to index type '{ [key in AB[S]]: true; }'.
5+
6+
7+
==== tests/cases/conformance/types/mapped/mappedTypeErrors2.ts (4 errors) ====
8+
// Repros from #17238
9+
10+
type AB = {
11+
a: 'a'
12+
b: 'a'
13+
};
14+
15+
type T1<K extends keyof AB> = { [key in AB[K]]: true };
16+
type T2<K extends 'a'|'b'> = T1<K>[K]; // Error
17+
~~~~~~~~
18+
!!! error TS2536: Type 'K' cannot be used to index type 'T1<K>'.
19+
20+
type R = AB[keyof AB]; // "a"
21+
type T3 = { [key in R]: true };
22+
type T4<K extends 'a'|'b'> = T3[K] // Error
23+
~~~~~
24+
!!! error TS2536: Type 'K' cannot be used to index type 'T3'.
25+
26+
type T5<S extends 'a'|'b'|'extra'> = {[key in AB[S]]: true}[S]; // Error
27+
~~~~~
28+
!!! error TS2536: Type 'S' cannot be used to index type 'AB'.
29+
30+
type T6<S extends 'a'|'b', L extends 'a'|'b'> = {[key in AB[S]]: true}[L]; // Error
31+
~~~~~~~~~~~~~~~~~~~~~~~~~
32+
!!! error TS2536: Type 'L' cannot be used to index type '{ [key in AB[S]]: true; }'.
33+
34+
type T7<S extends 'a'|'b', L extends 'a'> = {[key in AB[S]]: true}[L];
35+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [mappedTypeErrors2.ts]
2+
// Repros from #17238
3+
4+
type AB = {
5+
a: 'a'
6+
b: 'a'
7+
};
8+
9+
type T1<K extends keyof AB> = { [key in AB[K]]: true };
10+
type T2<K extends 'a'|'b'> = T1<K>[K]; // Error
11+
12+
type R = AB[keyof AB]; // "a"
13+
type T3 = { [key in R]: true };
14+
type T4<K extends 'a'|'b'> = T3[K] // Error
15+
16+
type T5<S extends 'a'|'b'|'extra'> = {[key in AB[S]]: true}[S]; // Error
17+
18+
type T6<S extends 'a'|'b', L extends 'a'|'b'> = {[key in AB[S]]: true}[L]; // Error
19+
20+
type T7<S extends 'a'|'b', L extends 'a'> = {[key in AB[S]]: true}[L];
21+
22+
23+
//// [mappedTypeErrors2.js]
24+
// Repros from #17238
25+
26+
27+
//// [mappedTypeErrors2.d.ts]
28+
declare type AB = {
29+
a: 'a';
30+
b: 'a';
31+
};
32+
declare type T1<K extends keyof AB> = {
33+
[key in AB[K]]: true;
34+
};
35+
declare type T2<K extends 'a' | 'b'> = T1<K>[K];
36+
declare type R = AB[keyof AB];
37+
declare type T3 = {
38+
[key in R]: true;
39+
};
40+
declare type T4<K extends 'a' | 'b'> = T3[K];
41+
declare type T5<S extends 'a' | 'b' | 'extra'> = {
42+
[key in AB[S]]: true;
43+
}[S];
44+
declare type T6<S extends 'a' | 'b', L extends 'a' | 'b'> = {
45+
[key in AB[S]]: true;
46+
}[L];
47+
declare type T7<S extends 'a' | 'b', L extends 'a'> = {
48+
[key in AB[S]]: true;
49+
}[L];
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// @strictNullChecks: true
2+
// @declaration: true
3+
4+
// Repros from #17238
5+
6+
type AB = {
7+
a: 'a'
8+
b: 'a'
9+
};
10+
11+
type T1<K extends keyof AB> = { [key in AB[K]]: true };
12+
type T2<K extends 'a'|'b'> = T1<K>[K]; // Error
13+
14+
type R = AB[keyof AB]; // "a"
15+
type T3 = { [key in R]: true };
16+
type T4<K extends 'a'|'b'> = T3[K] // Error
17+
18+
type T5<S extends 'a'|'b'|'extra'> = {[key in AB[S]]: true}[S]; // Error
19+
20+
type T6<S extends 'a'|'b', L extends 'a'|'b'> = {[key in AB[S]]: true}[L]; // Error
21+
22+
type T7<S extends 'a'|'b', L extends 'a'> = {[key in AB[S]]: true}[L];

0 commit comments

Comments
 (0)