Skip to content

Commit 76c23e4

Browse files
authored
Fixed binding element types coming from the out of tuple bounds under noUncheckedIndexedAccess (#52318)
1 parent 40208a8 commit 76c23e4

5 files changed

+185
-1
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22927,7 +22927,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2292722927
return propType;
2292822928
}
2292922929
if (everyType(type, isTupleType)) {
22930-
return mapType(type, t => getRestTypeOfTupleType(t as TupleTypeReference) || undefinedType);
22930+
return mapType(type, t => {
22931+
const tupleType = t as TupleTypeReference;
22932+
const restType = getRestTypeOfTupleType(tupleType);
22933+
if (!restType) {
22934+
return undefinedType;
22935+
}
22936+
if (compilerOptions.noUncheckedIndexedAccess &&
22937+
index >= tupleType.target.fixedLength + getEndElementCount(tupleType.target, ElementFlags.Fixed)) {
22938+
return getUnionType([restType, undefinedType]);
22939+
}
22940+
return restType;
22941+
});
2293122942
}
2293222943
return undefined;
2293322944
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
tests/cases/compiler/destructureTupleWithVariableElement.ts(9,1): error TS18048: 's1' is possibly 'undefined'.
2+
tests/cases/compiler/destructureTupleWithVariableElement.ts(10,1): error TS18048: 's2' is possibly 'undefined'.
3+
tests/cases/compiler/destructureTupleWithVariableElement.ts(18,1): error TS18048: 's5' is possibly 'undefined'.
4+
5+
6+
==== tests/cases/compiler/destructureTupleWithVariableElement.ts (3 errors) ====
7+
// repro from #52302
8+
9+
type NonEmptyStringArray = [string, ...Array<string>]
10+
11+
const strings: NonEmptyStringArray = ['one', 'two']
12+
const [s0, s1, s2] = strings;
13+
14+
s0.toUpperCase()
15+
s1.toUpperCase()
16+
~~
17+
!!! error TS18048: 's1' is possibly 'undefined'.
18+
s2.toUpperCase()
19+
~~
20+
!!! error TS18048: 's2' is possibly 'undefined'.
21+
22+
declare const strings2: [string, ...Array<string>, string]
23+
24+
const [s3, s4, s5] = strings2;
25+
26+
s3.toUpperCase()
27+
s4.toUpperCase()
28+
s5.toUpperCase()
29+
~~
30+
!!! error TS18048: 's5' is possibly 'undefined'.
31+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
=== tests/cases/compiler/destructureTupleWithVariableElement.ts ===
2+
// repro from #52302
3+
4+
type NonEmptyStringArray = [string, ...Array<string>]
5+
>NonEmptyStringArray : Symbol(NonEmptyStringArray, Decl(destructureTupleWithVariableElement.ts, 0, 0))
6+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
7+
8+
const strings: NonEmptyStringArray = ['one', 'two']
9+
>strings : Symbol(strings, Decl(destructureTupleWithVariableElement.ts, 4, 5))
10+
>NonEmptyStringArray : Symbol(NonEmptyStringArray, Decl(destructureTupleWithVariableElement.ts, 0, 0))
11+
12+
const [s0, s1, s2] = strings;
13+
>s0 : Symbol(s0, Decl(destructureTupleWithVariableElement.ts, 5, 7))
14+
>s1 : Symbol(s1, Decl(destructureTupleWithVariableElement.ts, 5, 10))
15+
>s2 : Symbol(s2, Decl(destructureTupleWithVariableElement.ts, 5, 14))
16+
>strings : Symbol(strings, Decl(destructureTupleWithVariableElement.ts, 4, 5))
17+
18+
s0.toUpperCase()
19+
>s0.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
20+
>s0 : Symbol(s0, Decl(destructureTupleWithVariableElement.ts, 5, 7))
21+
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
22+
23+
s1.toUpperCase()
24+
>s1.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
25+
>s1 : Symbol(s1, Decl(destructureTupleWithVariableElement.ts, 5, 10))
26+
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
27+
28+
s2.toUpperCase()
29+
>s2.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
30+
>s2 : Symbol(s2, Decl(destructureTupleWithVariableElement.ts, 5, 14))
31+
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
32+
33+
declare const strings2: [string, ...Array<string>, string]
34+
>strings2 : Symbol(strings2, Decl(destructureTupleWithVariableElement.ts, 11, 13))
35+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
36+
37+
const [s3, s4, s5] = strings2;
38+
>s3 : Symbol(s3, Decl(destructureTupleWithVariableElement.ts, 13, 7))
39+
>s4 : Symbol(s4, Decl(destructureTupleWithVariableElement.ts, 13, 10))
40+
>s5 : Symbol(s5, Decl(destructureTupleWithVariableElement.ts, 13, 14))
41+
>strings2 : Symbol(strings2, Decl(destructureTupleWithVariableElement.ts, 11, 13))
42+
43+
s3.toUpperCase()
44+
>s3.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
45+
>s3 : Symbol(s3, Decl(destructureTupleWithVariableElement.ts, 13, 7))
46+
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
47+
48+
s4.toUpperCase()
49+
>s4.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
50+
>s4 : Symbol(s4, Decl(destructureTupleWithVariableElement.ts, 13, 10))
51+
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
52+
53+
s5.toUpperCase()
54+
>s5.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
55+
>s5 : Symbol(s5, Decl(destructureTupleWithVariableElement.ts, 13, 14))
56+
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
57+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
=== tests/cases/compiler/destructureTupleWithVariableElement.ts ===
2+
// repro from #52302
3+
4+
type NonEmptyStringArray = [string, ...Array<string>]
5+
>NonEmptyStringArray : [string, ...string[]]
6+
7+
const strings: NonEmptyStringArray = ['one', 'two']
8+
>strings : [string, ...string[]]
9+
>['one', 'two'] : [string, string]
10+
>'one' : "one"
11+
>'two' : "two"
12+
13+
const [s0, s1, s2] = strings;
14+
>s0 : string
15+
>s1 : string | undefined
16+
>s2 : string | undefined
17+
>strings : [string, ...string[]]
18+
19+
s0.toUpperCase()
20+
>s0.toUpperCase() : string
21+
>s0.toUpperCase : () => string
22+
>s0 : string
23+
>toUpperCase : () => string
24+
25+
s1.toUpperCase()
26+
>s1.toUpperCase() : string
27+
>s1.toUpperCase : () => string
28+
>s1 : string | undefined
29+
>toUpperCase : () => string
30+
31+
s2.toUpperCase()
32+
>s2.toUpperCase() : string
33+
>s2.toUpperCase : () => string
34+
>s2 : string | undefined
35+
>toUpperCase : () => string
36+
37+
declare const strings2: [string, ...Array<string>, string]
38+
>strings2 : [string, ...string[], string]
39+
40+
const [s3, s4, s5] = strings2;
41+
>s3 : string
42+
>s4 : string | undefined
43+
>s5 : string | undefined
44+
>strings2 : [string, ...string[], string]
45+
46+
s3.toUpperCase()
47+
>s3.toUpperCase() : string
48+
>s3.toUpperCase : () => string
49+
>s3 : string
50+
>toUpperCase : () => string
51+
52+
s4.toUpperCase()
53+
>s4.toUpperCase() : string
54+
>s4.toUpperCase : () => string
55+
>s4 : string
56+
>toUpperCase : () => string
57+
58+
s5.toUpperCase()
59+
>s5.toUpperCase() : string
60+
>s5.toUpperCase : () => string
61+
>s5 : string | undefined
62+
>toUpperCase : () => string
63+
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+
// @noEmit: true
4+
5+
// repro from #52302
6+
7+
type NonEmptyStringArray = [string, ...Array<string>]
8+
9+
const strings: NonEmptyStringArray = ['one', 'two']
10+
const [s0, s1, s2] = strings;
11+
12+
s0.toUpperCase()
13+
s1.toUpperCase()
14+
s2.toUpperCase()
15+
16+
declare const strings2: [string, ...Array<string>, string]
17+
18+
const [s3, s4, s5] = strings2;
19+
20+
s3.toUpperCase()
21+
s4.toUpperCase()
22+
s5.toUpperCase()

0 commit comments

Comments
 (0)