Skip to content

check index access for fixed length tuple #26292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18293,6 +18293,13 @@ namespace ts {
error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal);
return errorType;
}
if (isTupleType(objectType) && !objectType.target.hasRestElement && isNumericLiteral(indexExpression)) {
const index = +indexExpression.text;
const maximumIndex = length(objectType.target.typeParameters);
Copy link
Contributor

@IllusionMH IllusionMH Aug 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed after message change.

Looks like there should be length(...) - 1 to get last available index (or add - 1 to error param). Otherwise errors looks off by 1 and confusing.

P.S. Thank you for PR. Glad to see that this will be in TS soon. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, my fault

if (index >= maximumIndex) {
error(indexExpression, Diagnostics.Index_0_is_out_of_bounds_in_tuple_of_length_1, index, maximumIndex);
}
}

return checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
}
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2437,6 +2437,10 @@
"category": "Error",
"code": 2732
},
"Index '{0}' is out-of-bounds in tuple of length {1}.": {
"category": "Error",
"code": 2733
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
40 changes: 40 additions & 0 deletions tests/baselines/reference/bestCommonTypeOfTuple.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(22,13): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(23,13): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(24,13): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts(25,13): error TS2733: Index '3' is out-of-bounds in tuple of length 3.


==== tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple.ts (4 errors) ====
function f1(x: number): string { return "foo"; }

function f2(x: number): number { return 10; }

function f3(x: number): boolean { return true; }

enum E1 { one }

enum E2 { two }


var t1: [(x: number) => string, (x: number) => number];
var t2: [E1, E2];
var t3: [number, any];
var t4: [E1, E2, number];

// no error
t1 = [f1, f2];
t2 = [E1.one, E2.two];
t3 = [5, undefined];
t4 = [E1.one, E2.two, 20];
var e1 = t1[2]; // {}
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var e2 = t2[2]; // {}
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var e3 = t3[2]; // any
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var e4 = t4[3]; // number
~
!!! error TS2733: Index '3' is out-of-bounds in tuple of length 3.
40 changes: 40 additions & 0 deletions tests/baselines/reference/bestCommonTypeOfTuple2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(17,14): error TS2733: Index '4' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(18,14): error TS2733: Index '4' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(19,14): error TS2733: Index '4' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(20,14): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts(21,14): error TS2733: Index '2' is out-of-bounds in tuple of length 2.


==== tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfTuple2.ts (5 errors) ====
interface base { }
interface base1 { i }
class C implements base { c }
class D implements base { d }
class E implements base { e }
class F extends C { f }

class C1 implements base1 { i = "foo"; c }
class D1 extends C1 { i = "bar"; d }

var t1: [C, base];
var t2: [C, D];
var t3: [C1, D1];
var t4: [base1, C1];
var t5: [C1, F]

var e11 = t1[4]; // base
~
!!! error TS2733: Index '4' is out-of-bounds in tuple of length 2.
var e21 = t2[4]; // {}
~
!!! error TS2733: Index '4' is out-of-bounds in tuple of length 2.
var e31 = t3[4]; // C1
~
!!! error TS2733: Index '4' is out-of-bounds in tuple of length 2.
var e41 = t4[2]; // base1
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var e51 = t5[2]; // {}
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.

5 changes: 4 additions & 1 deletion tests/baselines/reference/castingTuple.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ tests/cases/conformance/types/tuple/castingTuple.ts(14,15): error TS2352: Conver
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.
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.
Property '2' is missing in type '[C, D]'.
tests/cases/conformance/types/tuple/castingTuple.ts(20,33): error TS2733: Index '5' is out-of-bounds in tuple of length 3.
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.
Type 'string' is not comparable to type 'number'.
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.
Expand All @@ -15,7 +16,7 @@ tests/cases/conformance/types/tuple/castingTuple.ts(32,5): error TS2403: Subsequ
tests/cases/conformance/types/tuple/castingTuple.ts(33,1): error TS2304: Cannot find name 't4'.


==== tests/cases/conformance/types/tuple/castingTuple.ts (8 errors) ====
==== tests/cases/conformance/types/tuple/castingTuple.ts (9 errors) ====
interface I { }
class A { a = 10; }
class C implements I { c };
Expand Down Expand Up @@ -48,6 +49,8 @@ tests/cases/conformance/types/tuple/castingTuple.ts(33,1): error TS2304: Cannot
!!! error TS2352: Property '2' is missing in type '[C, D]'.
var eleFromCDA1 = classCDATuple[2]; // A
var eleFromCDA2 = classCDATuple[5]; // C | D | A
~
!!! error TS2733: Index '5' is out-of-bounds in tuple of length 3.
var t10: [E1, E2] = [E1.one, E2.one];
var t11 = <[number, number]>t10;
var array1 = <{}[]>emptyObjTuple;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion01.ts(2,11): error TS2733: Index '0' is out-of-bounds in tuple of length 0.


==== tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion01.ts (1 errors) ====
let x = <[]>[];
let y = x[0];
~
!!! error TS2733: Index '0' is out-of-bounds in tuple of length 0.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion02.ts(2,11): error TS2733: Index '0' is out-of-bounds in tuple of length 0.


==== tests/cases/conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion02.ts (1 errors) ====
let x = [] as [];
let y = x[0];
~
!!! error TS2733: Index '0' is out-of-bounds in tuple of length 0.
11 changes: 10 additions & 1 deletion tests/baselines/reference/genericCallWithTupleType.errors.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,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]'.
Types of property 'length' are incompatible.
Type '4' is not assignable to type '2'.
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(13,20): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(14,1): error TS2322: Type '{ a: string; }' is not assignable to type 'string | number'.
Type '{ a: string; }' is not assignable to type 'number'.
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(14,11): error TS2733: Index '3' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(15,20): error TS2733: Index '3' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(22,14): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(22,17): error TS2322: Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(23,14): error TS2322: Type '{}' is not assignable to type 'string'.
Expand All @@ -11,7 +14,7 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTup
Property '1' is missing in type '[{}]'.


==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts (7 errors) ====
==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts (10 errors) ====
interface I<T, U> {
tuple1: [T, U];
}
Expand All @@ -29,11 +32,17 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTup
!!! error TS2322: Types of property 'length' are incompatible.
!!! error TS2322: Type '4' is not assignable to type '2'.
var e3 = i1.tuple1[2]; // {}
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
i1.tuple1[3] = { a: "string" };
~~~~~~~~~~~~
!!! error TS2322: Type '{ a: string; }' is not assignable to type 'string | number'.
!!! error TS2322: Type '{ a: string; }' is not assignable to type 'number'.
~
!!! error TS2733: Index '3' is out-of-bounds in tuple of length 2.
var e4 = i1.tuple1[3]; // {}
~
!!! error TS2733: Index '3' is out-of-bounds in tuple of length 2.
i2.tuple1 = ["foo", 5];
i2.tuple1 = ["foo", "bar"];
i2.tuple1 = [5, "bar"];
Expand Down
47 changes: 47 additions & 0 deletions tests/baselines/reference/indexerWithTuple.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
tests/cases/conformance/types/tuple/indexerWithTuple.ts(11,25): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/tuple/indexerWithTuple.ts(17,27): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/tuple/indexerWithTuple.ts(20,30): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/tuple/indexerWithTuple.ts(28,30): error TS2733: Index '2' is out-of-bounds in tuple of length 2.


==== tests/cases/conformance/types/tuple/indexerWithTuple.ts (4 errors) ====
var strNumTuple: [string, number] = ["foo", 10];
var numTupleTuple: [number, [string, number]] = [10, ["bar", 20]];
var unionTuple1: [number, string| number] = [10, "foo"];
var unionTuple2: [boolean, string| number] = [true, "foo"];

// no error
var idx0 = 0;
var idx1 = 1;
var ele10 = strNumTuple[0]; // string
var ele11 = strNumTuple[1]; // number
var ele12 = strNumTuple[2]; // string | number
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var ele13 = strNumTuple[idx0]; // string | number
var ele14 = strNumTuple[idx1]; // string | number
var ele15 = strNumTuple["0"]; // string
var ele16 = strNumTuple["1"]; // number
var strNumTuple1 = numTupleTuple[1]; //[string, number];
var ele17 = numTupleTuple[2]; // number | [string, number]
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var eleUnion10 = unionTuple1[0]; // number
var eleUnion11 = unionTuple1[1]; // string | number
var eleUnion12 = unionTuple1[2]; // string | number
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var eleUnion13 = unionTuple1[idx0]; // string | number
var eleUnion14 = unionTuple1[idx1]; // string | number
var eleUnion15 = unionTuple1["0"]; // number
var eleUnion16 = unionTuple1["1"]; // string | number

var eleUnion20 = unionTuple2[0]; // boolean
var eleUnion21 = unionTuple2[1]; // string | number
var eleUnion22 = unionTuple2[2]; // string | number | boolean
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
var eleUnion23 = unionTuple2[idx0]; // string | number | boolean
var eleUnion24 = unionTuple2[idx1]; // string | number | boolean
var eleUnion25 = unionTuple2["0"]; // boolean
var eleUnion26 = unionTuple2["1"]; // string | number
21 changes: 21 additions & 0 deletions tests/baselines/reference/tupleLengthCheck.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
tests/cases/conformance/types/tuple/tupleLengthCheck.ts(5,14): error TS2733: Index '2' is out-of-bounds in tuple of length 2.
tests/cases/conformance/types/tuple/tupleLengthCheck.ts(6,14): error TS2733: Index '1000' is out-of-bounds in tuple of length 2.


==== tests/cases/conformance/types/tuple/tupleLengthCheck.ts (2 errors) ====
declare const a: [number, string]
declare const rest: [number, string, ...boolean[]]

const a1 = a[1]
const a2 = a[2]
~
!!! error TS2733: Index '2' is out-of-bounds in tuple of length 2.
const a3 = a[1000]
~~~~
!!! error TS2733: Index '1000' is out-of-bounds in tuple of length 2.

const a4 = rest[1]
const a5 = rest[2]
const a6 = rest[3]
const a7 = rest[1000]

22 changes: 22 additions & 0 deletions tests/baselines/reference/tupleLengthCheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//// [tupleLengthCheck.ts]
declare const a: [number, string]
declare const rest: [number, string, ...boolean[]]

const a1 = a[1]
const a2 = a[2]
const a3 = a[1000]

const a4 = rest[1]
const a5 = rest[2]
const a6 = rest[3]
const a7 = rest[1000]


//// [tupleLengthCheck.js]
var a1 = a[1];
var a2 = a[2];
var a3 = a[1000];
var a4 = rest[1];
var a5 = rest[2];
var a6 = rest[3];
var a7 = rest[1000];
37 changes: 37 additions & 0 deletions tests/baselines/reference/tupleLengthCheck.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=== tests/cases/conformance/types/tuple/tupleLengthCheck.ts ===
declare const a: [number, string]
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))

declare const rest: [number, string, ...boolean[]]
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))

const a1 = a[1]
>a1 : Symbol(a1, Decl(tupleLengthCheck.ts, 3, 5))
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))
>1 : Symbol(1)

const a2 = a[2]
>a2 : Symbol(a2, Decl(tupleLengthCheck.ts, 4, 5))
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))

const a3 = a[1000]
>a3 : Symbol(a3, Decl(tupleLengthCheck.ts, 5, 5))
>a : Symbol(a, Decl(tupleLengthCheck.ts, 0, 13))

const a4 = rest[1]
>a4 : Symbol(a4, Decl(tupleLengthCheck.ts, 7, 5))
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))
>1 : Symbol(1)

const a5 = rest[2]
>a5 : Symbol(a5, Decl(tupleLengthCheck.ts, 8, 5))
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))

const a6 = rest[3]
>a6 : Symbol(a6, Decl(tupleLengthCheck.ts, 9, 5))
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))

const a7 = rest[1000]
>a7 : Symbol(a7, Decl(tupleLengthCheck.ts, 10, 5))
>rest : Symbol(rest, Decl(tupleLengthCheck.ts, 1, 13))

49 changes: 49 additions & 0 deletions tests/baselines/reference/tupleLengthCheck.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
=== tests/cases/conformance/types/tuple/tupleLengthCheck.ts ===
declare const a: [number, string]
>a : [number, string]

declare const rest: [number, string, ...boolean[]]
>rest : [number, string, ...boolean[]]

const a1 = a[1]
>a1 : string
>a[1] : string
>a : [number, string]
>1 : 1

const a2 = a[2]
>a2 : string | number
>a[2] : string | number
>a : [number, string]
>2 : 2

const a3 = a[1000]
>a3 : string | number
>a[1000] : string | number
>a : [number, string]
>1000 : 1000

const a4 = rest[1]
>a4 : string
>rest[1] : string
>rest : [number, string, ...boolean[]]
>1 : 1

const a5 = rest[2]
>a5 : boolean
>rest[2] : boolean
>rest : [number, string, ...boolean[]]
>2 : 2

const a6 = rest[3]
>a6 : boolean
>rest[3] : boolean
>rest : [number, string, ...boolean[]]
>3 : 3

const a7 = rest[1000]
>a7 : boolean
>rest[1000] : boolean
>rest : [number, string, ...boolean[]]
>1000 : 1000

Loading