Skip to content

Commit e90f645

Browse files
committed
check index access for fixed length tuple
1 parent 937afab commit e90f645

15 files changed

+315
-3
lines changed

src/compiler/checker.ts

+7
Original file line numberDiff line numberDiff line change
@@ -18293,6 +18293,13 @@ namespace ts {
1829318293
error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal);
1829418294
return errorType;
1829518295
}
18296+
if (isTupleType(objectType) && !objectType.target.hasRestElement && isNumericLiteral(indexExpression)) {
18297+
const index = +indexExpression.text;
18298+
const maximumIndex = length(objectType.target.typeParameters) - 1;
18299+
if (index > maximumIndex) {
18300+
error(indexExpression, Diagnostics.Index_0_is_out_of_bounds_in_tuple_of_length_1, index, maximumIndex);
18301+
}
18302+
}
1829618303

1829718304
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
1829818305
}

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,10 @@
24372437
"category": "Error",
24382438
"code": 2732
24392439
},
2440+
"Index '{0}' is out-of-bounds in tuple of length {1}.": {
2441+
"category": "Error",
2442+
"code": 2733
2443+
},
24402444

24412445
"Import declaration '{0}' is using private name '{1}'.": {
24422446
"category": "Error",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(22,13): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
2+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(23,13): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
3+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(24,13): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
4+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(25,13): error TS2733: Index '3' is out-of-bounds in tuple of length 2.
5+
6+
7+
==== tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts (4 errors) ====
8+
function f1(x: number): string { return "foo"; }
9+
10+
function f2(x: number): number { return 10; }
11+
12+
function f3(x: number): boolean { return true; }
13+
14+
enum E1 { one }
15+
16+
enum E2 { two }
17+
18+
19+
var t1: [(x: number) => string, (x: number) => number];
20+
var t2: [E1, E2];
21+
var t3: [number, any];
22+
var t4: [E1, E2, number];
23+
24+
// no error
25+
t1 = [f1, f2];
26+
t2 = [E1.one, E2.two];
27+
t3 = [5, undefined];
28+
t4 = [E1.one, E2.two, 20];
29+
var e1 = t1[2]; // {}
30+
~
31+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
32+
var e2 = t2[2]; // {}
33+
~
34+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
35+
var e3 = t3[2]; // any
36+
~
37+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
38+
var e4 = t4[3]; // number
39+
~
40+
!!! error TS2733: Index '3' is out-of-bounds in tuple of length 2.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(17,14): error TS2733: Index '4' is out-of-bounds in tuple of length 1.
2+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(18,14): error TS2733: Index '4' is out-of-bounds in tuple of length 1.
3+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(19,14): error TS2733: Index '4' is out-of-bounds in tuple of length 1.
4+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(20,14): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
5+
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(21,14): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
6+
7+
8+
==== tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts (5 errors) ====
9+
interface base { }
10+
interface base1 { i }
11+
class C implements base { c }
12+
class D implements base { d }
13+
class E implements base { e }
14+
class F extends C { f }
15+
16+
class C1 implements base1 { i = "foo"; c }
17+
class D1 extends C1 { i = "bar"; d }
18+
19+
var t1: [C, base];
20+
var t2: [C, D];
21+
var t3: [C1, D1];
22+
var t4: [base1, C1];
23+
var t5: [C1, F]
24+
25+
var e11 = t1[4]; // base
26+
~
27+
!!! error TS2733: Index '4' is out-of-bounds in tuple of length 1.
28+
var e21 = t2[4]; // {}
29+
~
30+
!!! error TS2733: Index '4' is out-of-bounds in tuple of length 1.
31+
var e31 = t3[4]; // C1
32+
~
33+
!!! error TS2733: Index '4' is out-of-bounds in tuple of length 1.
34+
var e41 = t4[2]; // base1
35+
~
36+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
37+
var e51 = t5[2]; // {}
38+
~
39+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
40+

tests/baselines/reference/castingTuple.errors.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ tests/cases/conformance/types/tuple/castingTuple.ts(14,15): error TS2352: Conver
66
tests/cases/conformance/types/tuple/castingTuple.ts(15,14): error TS2352: Conversion of type '[number, string]' to type '[number, string, boolean]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
77
tests/cases/conformance/types/tuple/castingTuple.ts(18,21): error TS2352: Conversion of type '[C, D]' to type '[C, D, A]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
88
Property '2' is missing in type '[C, D]'.
9+
tests/cases/conformance/types/tuple/castingTuple.ts(20,33): error TS2733: Index '5' is out-of-bounds in tuple of length 2.
910
tests/cases/conformance/types/tuple/castingTuple.ts(30,10): error TS2352: Conversion of type '[number, string]' to type '[number, number]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
1011
Type 'string' is not comparable to type 'number'.
1112
tests/cases/conformance/types/tuple/castingTuple.ts(31,10): error TS2352: Conversion of type '[C, D]' to type '[A, I]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
@@ -15,7 +16,7 @@ tests/cases/conformance/types/tuple/castingTuple.ts(32,5): error TS2403: Subsequ
1516
tests/cases/conformance/types/tuple/castingTuple.ts(33,1): error TS2304: Cannot find name 't4'.
1617

1718

18-
==== tests/cases/conformance/types/tuple/castingTuple.ts (8 errors) ====
19+
==== tests/cases/conformance/types/tuple/castingTuple.ts (9 errors) ====
1920
interface I { }
2021
class A { a = 10; }
2122
class C implements I { c };
@@ -48,6 +49,8 @@ tests/cases/conformance/types/tuple/castingTuple.ts(33,1): error TS2304: Cannot
4849
!!! error TS2352: Property '2' is missing in type '[C, D]'.
4950
var eleFromCDA1 = classCDATuple[2]; // A
5051
var eleFromCDA2 = classCDATuple[5]; // C | D | A
52+
~
53+
!!! error TS2733: Index '5' is out-of-bounds in tuple of length 2.
5154
var t10: [E1, E2] = [E1.one, E2.one];
5255
var t11 = <[number, number]>t10;
5356
var array1 = <{}[]>emptyObjTuple;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion01.ts(2,11): error TS2733: Index '0' is out-of-bounds in tuple of length -1.
2+
3+
4+
==== tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion01.ts (1 errors) ====
5+
let x = <[]>[];
6+
let y = x[0];
7+
~
8+
!!! error TS2733: Index '0' is out-of-bounds in tuple of length -1.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion02.ts(2,11): error TS2733: Index '0' is out-of-bounds in tuple of length -1.
2+
3+
4+
==== tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion02.ts (1 errors) ====
5+
let x = [] as [];
6+
let y = x[0];
7+
~
8+
!!! error TS2733: Index '0' is out-of-bounds in tuple of length -1.

tests/baselines/reference/genericCallWithTupleType.errors.txt

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(12,1): error TS2322: Type '[string, number, boolean, boolean]' is not assignable to type '[string, number]'.
22
Types of property 'length' are incompatible.
33
Type '4' is not assignable to type '2'.
4+
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(13,20): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
45
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(14,1): error TS2322: Type '{ a: string; }' is not assignable to type 'string | number'.
56
Type '{ a: string; }' is not assignable to type 'number'.
7+
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(14,11): error TS2733: Index '3' is out-of-bounds in tuple of length 1.
8+
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(15,20): error TS2733: Index '3' is out-of-bounds in tuple of length 1.
69
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(22,14): error TS2322: Type 'number' is not assignable to type 'string'.
710
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(22,17): error TS2322: Type 'string' is not assignable to type 'number'.
811
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(23,14): error TS2322: Type '{}' is not assignable to type 'string'.
@@ -11,7 +14,7 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTup
1114
Property '1' is missing in type '[{}]'.
1215

1316

14-
==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts (7 errors) ====
17+
==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts (10 errors) ====
1518
interface I<T, U> {
1619
tuple1: [T, U];
1720
}
@@ -29,11 +32,17 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTup
2932
!!! error TS2322: Types of property 'length' are incompatible.
3033
!!! error TS2322: Type '4' is not assignable to type '2'.
3134
var e3 = i1.tuple1[2]; // {}
35+
~
36+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
3237
i1.tuple1[3] = { a: "string" };
3338
~~~~~~~~~~~~
3439
!!! error TS2322: Type '{ a: string; }' is not assignable to type 'string | number'.
3540
!!! error TS2322: Type '{ a: string; }' is not assignable to type 'number'.
41+
~
42+
!!! error TS2733: Index '3' is out-of-bounds in tuple of length 1.
3643
var e4 = i1.tuple1[3]; // {}
44+
~
45+
!!! error TS2733: Index '3' is out-of-bounds in tuple of length 1.
3746
i2.tuple1 = ["foo", 5];
3847
i2.tuple1 = ["foo", "bar"];
3948
i2.tuple1 = [5, "bar"];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
tests/cases/conformance/types/tuple/indexerWithTuple.ts(11,25): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
2+
tests/cases/conformance/types/tuple/indexerWithTuple.ts(17,27): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
3+
tests/cases/conformance/types/tuple/indexerWithTuple.ts(20,30): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
4+
tests/cases/conformance/types/tuple/indexerWithTuple.ts(28,30): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
5+
6+
7+
==== tests/cases/conformance/types/tuple/indexerWithTuple.ts (4 errors) ====
8+
var strNumTuple: [string, number] = ["foo", 10];
9+
var numTupleTuple: [number, [string, number]] = [10, ["bar", 20]];
10+
var unionTuple1: [number, string| number] = [10, "foo"];
11+
var unionTuple2: [boolean, string| number] = [true, "foo"];
12+
13+
// no error
14+
var idx0 = 0;
15+
var idx1 = 1;
16+
var ele10 = strNumTuple[0]; // string
17+
var ele11 = strNumTuple[1]; // number
18+
var ele12 = strNumTuple[2]; // string | number
19+
~
20+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
21+
var ele13 = strNumTuple[idx0]; // string | number
22+
var ele14 = strNumTuple[idx1]; // string | number
23+
var ele15 = strNumTuple["0"]; // string
24+
var ele16 = strNumTuple["1"]; // number
25+
var strNumTuple1 = numTupleTuple[1]; //[string, number];
26+
var ele17 = numTupleTuple[2]; // number | [string, number]
27+
~
28+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
29+
var eleUnion10 = unionTuple1[0]; // number
30+
var eleUnion11 = unionTuple1[1]; // string | number
31+
var eleUnion12 = unionTuple1[2]; // string | number
32+
~
33+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
34+
var eleUnion13 = unionTuple1[idx0]; // string | number
35+
var eleUnion14 = unionTuple1[idx1]; // string | number
36+
var eleUnion15 = unionTuple1["0"]; // number
37+
var eleUnion16 = unionTuple1["1"]; // string | number
38+
39+
var eleUnion20 = unionTuple2[0]; // boolean
40+
var eleUnion21 = unionTuple2[1]; // string | number
41+
var eleUnion22 = unionTuple2[2]; // string | number | boolean
42+
~
43+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
44+
var eleUnion23 = unionTuple2[idx0]; // string | number | boolean
45+
var eleUnion24 = unionTuple2[idx1]; // string | number | boolean
46+
var eleUnion25 = unionTuple2["0"]; // boolean
47+
var eleUnion26 = unionTuple2["1"]; // string | number
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/conformance/types/tuple/tupleLengthCheck.ts(5,14): error TS2733: Index '2' is out-of-bounds in tuple of length 1.
2+
tests/cases/conformance/types/tuple/tupleLengthCheck.ts(6,14): error TS2733: Index '1000' is out-of-bounds in tuple of length 1.
3+
4+
5+
==== tests/cases/conformance/types/tuple/tupleLengthCheck.ts (2 errors) ====
6+
declare const a: [number, string]
7+
declare const rest: [number, string, ...boolean[]]
8+
9+
const a1 = a[1]
10+
const a2 = a[2]
11+
~
12+
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 1.
13+
const a3 = a[1000]
14+
~~~~
15+
!!! error TS2733: Index '1000' is out-of-bounds in tuple of length 1.
16+
17+
const a4 = rest[1]
18+
const a5 = rest[2]
19+
const a6 = rest[3]
20+
const a7 = rest[1000]
21+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [tupleLengthCheck.ts]
2+
declare const a: [number, string]
3+
declare const rest: [number, string, ...boolean[]]
4+
5+
const a1 = a[1]
6+
const a2 = a[2]
7+
const a3 = a[1000]
8+
9+
const a4 = rest[1]
10+
const a5 = rest[2]
11+
const a6 = rest[3]
12+
const a7 = rest[1000]
13+
14+
15+
//// [tupleLengthCheck.js]
16+
var a1 = a[1];
17+
var a2 = a[2];
18+
var a3 = a[1000];
19+
var a4 = rest[1];
20+
var a5 = rest[2];
21+
var a6 = rest[3];
22+
var a7 = rest[1000];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/conformance/types/tuple/tupleLengthCheck.ts ===
2+
declare const a: [number, string]
3+
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))
4+
5+
declare const rest: [number, string, ...boolean[]]
6+
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))
7+
8+
const a1 = a[1]
9+
>a1 : Symbol(a1, Decl(tupleLengthCheck.ts, 3, 5))
10+
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))
11+
>1 : Symbol(1)
12+
13+
const a2 = a[2]
14+
>a2 : Symbol(a2, Decl(tupleLengthCheck.ts, 4, 5))
15+
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))
16+
17+
const a3 = a[1000]
18+
>a3 : Symbol(a3, Decl(tupleLengthCheck.ts, 5, 5))
19+
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))
20+
21+
const a4 = rest[1]
22+
>a4 : Symbol(a4, Decl(tupleLengthCheck.ts, 7, 5))
23+
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))
24+
>1 : Symbol(1)
25+
26+
const a5 = rest[2]
27+
>a5 : Symbol(a5, Decl(tupleLengthCheck.ts, 8, 5))
28+
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))
29+
30+
const a6 = rest[3]
31+
>a6 : Symbol(a6, Decl(tupleLengthCheck.ts, 9, 5))
32+
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))
33+
34+
const a7 = rest[1000]
35+
>a7 : Symbol(a7, Decl(tupleLengthCheck.ts, 10, 5))
36+
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))
37+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
=== tests/cases/conformance/types/tuple/tupleLengthCheck.ts ===
2+
declare const a: [number, string]
3+
>a : [number, string]
4+
5+
declare const rest: [number, string, ...boolean[]]
6+
>rest : [number, string, ...boolean[]]
7+
8+
const a1 = a[1]
9+
>a1 : string
10+
>a[1] : string
11+
>a : [number, string]
12+
>1 : 1
13+
14+
const a2 = a[2]
15+
>a2 : string | number
16+
>a[2] : string | number
17+
>a : [number, string]
18+
>2 : 2
19+
20+
const a3 = a[1000]
21+
>a3 : string | number
22+
>a[1000] : string | number
23+
>a : [number, string]
24+
>1000 : 1000
25+
26+
const a4 = rest[1]
27+
>a4 : string
28+
>rest[1] : string
29+
>rest : [number, string, ...boolean[]]
30+
>1 : 1
31+
32+
const a5 = rest[2]
33+
>a5 : boolean
34+
>rest[2] : boolean
35+
>rest : [number, string, ...boolean[]]
36+
>2 : 2
37+
38+
const a6 = rest[3]
39+
>a6 : boolean
40+
>rest[3] : boolean
41+
>rest : [number, string, ...boolean[]]
42+
>3 : 3
43+
44+
const a7 = rest[1000]
45+
>a7 : boolean
46+
>rest[1000] : boolean
47+
>rest : [number, string, ...boolean[]]
48+
>1000 : 1000
49+

0 commit comments

Comments
 (0)