Skip to content

Commit aedd1b1

Browse files
Correctly compute noUncheckedIndexedAccess effects on compound/increment/decrement assignments (#58239)
1 parent 3480321 commit aedd1b1

6 files changed

+356
-4
lines changed

src/compiler/checker.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33031,7 +33031,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3303133031
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
3303233032
}
3303333033

33034-
propType = (compilerOptions.noUncheckedIndexedAccess && !isAssignmentTarget(node)) ? getUnionType([indexInfo.type, missingType]) : indexInfo.type;
33034+
propType = indexInfo.type;
33035+
if (compilerOptions.noUncheckedIndexedAccess && getAssignmentTargetKind(node) !== AssignmentKind.Definite) {
33036+
propType = getUnionType([propType, missingType]);
33037+
}
3303533038
if (compilerOptions.noPropertyAccessFromIndexSignature && isPropertyAccessExpression(node)) {
3303633039
error(right, Diagnostics.Property_0_comes_from_an_index_signature_so_it_must_be_accessed_with_0, unescapeLeadingUnderscores(right.escapedText));
3303733040
}
@@ -33626,9 +33629,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3362633629
}
3362733630

3362833631
const effectiveIndexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType;
33629-
const accessFlags = isAssignmentTarget(node) ?
33630-
AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) :
33631-
AccessFlags.ExpressionPosition;
33632+
const assignmentTargetKind = getAssignmentTargetKind(node);
33633+
let accessFlags: AccessFlags;
33634+
if (assignmentTargetKind === AssignmentKind.None) {
33635+
accessFlags = AccessFlags.ExpressionPosition;
33636+
}
33637+
else {
33638+
accessFlags = AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0);
33639+
if (assignmentTargetKind === AssignmentKind.Compound) {
33640+
accessFlags |= AccessFlags.ExpressionPosition;
33641+
}
33642+
}
3363233643
const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, accessFlags, node) || errorType;
3363333644
return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, getNodeLinks(node).resolvedSymbol, indexedAccessType, indexExpression, checkMode), node);
3363433645
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
noUncheckedIndexedAccessCompoundAssignments.ts(3,1): error TS18048: 'stringMap.foo' is possibly 'undefined'.
2+
noUncheckedIndexedAccessCompoundAssignments.ts(4,3): error TS18048: 'stringMap.foo' is possibly 'undefined'.
3+
noUncheckedIndexedAccessCompoundAssignments.ts(5,1): error TS18048: 'stringMap.foo' is possibly 'undefined'.
4+
noUncheckedIndexedAccessCompoundAssignments.ts(6,1): error TS18048: 'stringMap.foo' is possibly 'undefined'.
5+
noUncheckedIndexedAccessCompoundAssignments.ts(7,3): error TS2532: Object is possibly 'undefined'.
6+
noUncheckedIndexedAccessCompoundAssignments.ts(8,1): error TS2532: Object is possibly 'undefined'.
7+
noUncheckedIndexedAccessCompoundAssignments.ts(9,3): error TS2532: Object is possibly 'undefined'.
8+
noUncheckedIndexedAccessCompoundAssignments.ts(10,1): error TS2532: Object is possibly 'undefined'.
9+
noUncheckedIndexedAccessCompoundAssignments.ts(11,1): error TS2532: Object is possibly 'undefined'.
10+
noUncheckedIndexedAccessCompoundAssignments.ts(12,1): error TS2532: Object is possibly 'undefined'.
11+
noUncheckedIndexedAccessCompoundAssignments.ts(13,1): error TS2532: Object is possibly 'undefined'.
12+
noUncheckedIndexedAccessCompoundAssignments.ts(14,1): error TS2532: Object is possibly 'undefined'.
13+
14+
15+
==== noUncheckedIndexedAccessCompoundAssignments.ts (12 errors) ====
16+
// Each line should have one error
17+
// for a total of 12 errors
18+
stringMap.foo++;
19+
~~~~~~~~~~~~~
20+
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
21+
--stringMap.foo;
22+
~~~~~~~~~~~~~
23+
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
24+
stringMap.foo += 1;
25+
~~~~~~~~~~~~~
26+
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
27+
stringMap.foo *= 1;
28+
~~~~~~~~~~~~~
29+
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
30+
++stringMap['foo'];
31+
~~~~~~~~~~~~~~~~
32+
!!! error TS2532: Object is possibly 'undefined'.
33+
stringMap['foo']--;
34+
~~~~~~~~~~~~~~~~
35+
!!! error TS2532: Object is possibly 'undefined'.
36+
++stringMap[s];
37+
~~~~~~~~~~~~
38+
!!! error TS2532: Object is possibly 'undefined'.
39+
stringMap[s]--;
40+
~~~~~~~~~~~~
41+
!!! error TS2532: Object is possibly 'undefined'.
42+
numberMap[32]++;
43+
~~~~~~~~~~~~~
44+
!!! error TS2532: Object is possibly 'undefined'.
45+
numberMap[32] += 1;
46+
~~~~~~~~~~~~~
47+
!!! error TS2532: Object is possibly 'undefined'.
48+
numberMap[n]++;
49+
~~~~~~~~~~~~
50+
!!! error TS2532: Object is possibly 'undefined'.
51+
numberMap[n] += 1;
52+
~~~~~~~~~~~~
53+
!!! error TS2532: Object is possibly 'undefined'.
54+
55+
declare const stringMap: { [s: string]: number };
56+
declare const s: string;
57+
declare const numberMap: { [n: number]: number };
58+
declare const n: number;
59+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [tests/cases/compiler/noUncheckedIndexedAccessCompoundAssignments.ts] ////
2+
3+
//// [noUncheckedIndexedAccessCompoundAssignments.ts]
4+
// Each line should have one error
5+
// for a total of 12 errors
6+
stringMap.foo++;
7+
--stringMap.foo;
8+
stringMap.foo += 1;
9+
stringMap.foo *= 1;
10+
++stringMap['foo'];
11+
stringMap['foo']--;
12+
++stringMap[s];
13+
stringMap[s]--;
14+
numberMap[32]++;
15+
numberMap[32] += 1;
16+
numberMap[n]++;
17+
numberMap[n] += 1;
18+
19+
declare const stringMap: { [s: string]: number };
20+
declare const s: string;
21+
declare const numberMap: { [n: number]: number };
22+
declare const n: number;
23+
24+
25+
//// [noUncheckedIndexedAccessCompoundAssignments.js]
26+
"use strict";
27+
// Each line should have one error
28+
// for a total of 12 errors
29+
stringMap.foo++;
30+
--stringMap.foo;
31+
stringMap.foo += 1;
32+
stringMap.foo *= 1;
33+
++stringMap['foo'];
34+
stringMap['foo']--;
35+
++stringMap[s];
36+
stringMap[s]--;
37+
numberMap[32]++;
38+
numberMap[32] += 1;
39+
numberMap[n]++;
40+
numberMap[n] += 1;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [tests/cases/compiler/noUncheckedIndexedAccessCompoundAssignments.ts] ////
2+
3+
=== noUncheckedIndexedAccessCompoundAssignments.ts ===
4+
// Each line should have one error
5+
// for a total of 12 errors
6+
stringMap.foo++;
7+
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
8+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
9+
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
10+
11+
--stringMap.foo;
12+
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
13+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
14+
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
15+
16+
stringMap.foo += 1;
17+
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
18+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
19+
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
20+
21+
stringMap.foo *= 1;
22+
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
23+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
24+
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
25+
26+
++stringMap['foo'];
27+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
28+
29+
stringMap['foo']--;
30+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
31+
32+
++stringMap[s];
33+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
34+
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 16, 13))
35+
36+
stringMap[s]--;
37+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
38+
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 16, 13))
39+
40+
numberMap[32]++;
41+
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
42+
43+
numberMap[32] += 1;
44+
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
45+
46+
numberMap[n]++;
47+
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
48+
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 18, 13))
49+
50+
numberMap[n] += 1;
51+
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
52+
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 18, 13))
53+
54+
declare const stringMap: { [s: string]: number };
55+
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
56+
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 28))
57+
58+
declare const s: string;
59+
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 16, 13))
60+
61+
declare const numberMap: { [n: number]: number };
62+
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
63+
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 28))
64+
65+
declare const n: number;
66+
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 18, 13))
67+
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//// [tests/cases/compiler/noUncheckedIndexedAccessCompoundAssignments.ts] ////
2+
3+
=== noUncheckedIndexedAccessCompoundAssignments.ts ===
4+
// Each line should have one error
5+
// for a total of 12 errors
6+
stringMap.foo++;
7+
>stringMap.foo++ : number
8+
> : ^^^^^^
9+
>stringMap.foo : number | undefined
10+
> : ^^^^^^^^^^^^^^^^^^
11+
>stringMap : { [s: string]: number; }
12+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
13+
>foo : number | undefined
14+
> : ^^^^^^^^^^^^^^^^^^
15+
16+
--stringMap.foo;
17+
>--stringMap.foo : number
18+
> : ^^^^^^
19+
>stringMap.foo : number | undefined
20+
> : ^^^^^^^^^^^^^^^^^^
21+
>stringMap : { [s: string]: number; }
22+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
23+
>foo : number | undefined
24+
> : ^^^^^^^^^^^^^^^^^^
25+
26+
stringMap.foo += 1;
27+
>stringMap.foo += 1 : number
28+
> : ^^^^^^
29+
>stringMap.foo : number | undefined
30+
> : ^^^^^^^^^^^^^^^^^^
31+
>stringMap : { [s: string]: number; }
32+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
33+
>foo : number | undefined
34+
> : ^^^^^^^^^^^^^^^^^^
35+
>1 : 1
36+
> : ^
37+
38+
stringMap.foo *= 1;
39+
>stringMap.foo *= 1 : number
40+
> : ^^^^^^
41+
>stringMap.foo : number | undefined
42+
> : ^^^^^^^^^^^^^^^^^^
43+
>stringMap : { [s: string]: number; }
44+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
45+
>foo : number | undefined
46+
> : ^^^^^^^^^^^^^^^^^^
47+
>1 : 1
48+
> : ^
49+
50+
++stringMap['foo'];
51+
>++stringMap['foo'] : number
52+
> : ^^^^^^
53+
>stringMap['foo'] : number | undefined
54+
> : ^^^^^^^^^^^^^^^^^^
55+
>stringMap : { [s: string]: number; }
56+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
57+
>'foo' : "foo"
58+
> : ^^^^^
59+
60+
stringMap['foo']--;
61+
>stringMap['foo']-- : number
62+
> : ^^^^^^
63+
>stringMap['foo'] : number | undefined
64+
> : ^^^^^^^^^^^^^^^^^^
65+
>stringMap : { [s: string]: number; }
66+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
67+
>'foo' : "foo"
68+
> : ^^^^^
69+
70+
++stringMap[s];
71+
>++stringMap[s] : number
72+
> : ^^^^^^
73+
>stringMap[s] : number | undefined
74+
> : ^^^^^^^^^^^^^^^^^^
75+
>stringMap : { [s: string]: number; }
76+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
77+
>s : string
78+
> : ^^^^^^
79+
80+
stringMap[s]--;
81+
>stringMap[s]-- : number
82+
> : ^^^^^^
83+
>stringMap[s] : number | undefined
84+
> : ^^^^^^^^^^^^^^^^^^
85+
>stringMap : { [s: string]: number; }
86+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
87+
>s : string
88+
> : ^^^^^^
89+
90+
numberMap[32]++;
91+
>numberMap[32]++ : number
92+
> : ^^^^^^
93+
>numberMap[32] : number | undefined
94+
> : ^^^^^^^^^^^^^^^^^^
95+
>numberMap : { [n: number]: number; }
96+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
97+
>32 : 32
98+
> : ^^
99+
100+
numberMap[32] += 1;
101+
>numberMap[32] += 1 : number
102+
> : ^^^^^^
103+
>numberMap[32] : number | undefined
104+
> : ^^^^^^^^^^^^^^^^^^
105+
>numberMap : { [n: number]: number; }
106+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
107+
>32 : 32
108+
> : ^^
109+
>1 : 1
110+
> : ^
111+
112+
numberMap[n]++;
113+
>numberMap[n]++ : number
114+
> : ^^^^^^
115+
>numberMap[n] : number | undefined
116+
> : ^^^^^^^^^^^^^^^^^^
117+
>numberMap : { [n: number]: number; }
118+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
119+
>n : number
120+
> : ^^^^^^
121+
122+
numberMap[n] += 1;
123+
>numberMap[n] += 1 : number
124+
> : ^^^^^^
125+
>numberMap[n] : number | undefined
126+
> : ^^^^^^^^^^^^^^^^^^
127+
>numberMap : { [n: number]: number; }
128+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
129+
>n : number
130+
> : ^^^^^^
131+
>1 : 1
132+
> : ^
133+
134+
declare const stringMap: { [s: string]: number };
135+
>stringMap : { [s: string]: number; }
136+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
137+
>s : string
138+
> : ^^^^^^
139+
140+
declare const s: string;
141+
>s : string
142+
> : ^^^^^^
143+
144+
declare const numberMap: { [n: number]: number };
145+
>numberMap : { [n: number]: number; }
146+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
147+
>n : number
148+
> : ^^^^^^
149+
150+
declare const n: number;
151+
>n : number
152+
> : ^^^^^^
153+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// @strict: true
2+
// @noUncheckedIndexedAccess: true
3+
4+
// Each line should have one error
5+
// for a total of 12 errors
6+
stringMap.foo++;
7+
--stringMap.foo;
8+
stringMap.foo += 1;
9+
stringMap.foo *= 1;
10+
++stringMap['foo'];
11+
stringMap['foo']--;
12+
++stringMap[s];
13+
stringMap[s]--;
14+
numberMap[32]++;
15+
numberMap[32] += 1;
16+
numberMap[n]++;
17+
numberMap[n] += 1;
18+
19+
declare const stringMap: { [s: string]: number };
20+
declare const s: string;
21+
declare const numberMap: { [n: number]: number };
22+
declare const n: number;

0 commit comments

Comments
 (0)