Skip to content

Commit 11b9118

Browse files
committed
Merge pull request #428 from Microsoft/tupleTypes
Adding support for tuple types (e.g. [number, string])
2 parents b9ebb36 + b4cddc3 commit 11b9118

File tree

9 files changed

+324
-43
lines changed

9 files changed

+324
-43
lines changed

src/compiler/checker.ts

+125-28
Large diffs are not rendered by default.

src/compiler/core.ts

+16-10
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ module ts {
1919

2020
export function contains<T>(array: T[], value: T): boolean {
2121
if (array) {
22-
var len = array.length;
23-
for (var i = 0; i < len; i++) {
22+
for (var i = 0, len = array.length; i < len; i++) {
2423
if (array[i] === value) {
2524
return true;
2625
}
@@ -31,8 +30,7 @@ module ts {
3130

3231
export function indexOf<T>(array: T[], value: T): number {
3332
if (array) {
34-
var len = array.length;
35-
for (var i = 0; i < len; i++) {
33+
for (var i = 0, len = array.length; i < len; i++) {
3634
if (array[i] === value) {
3735
return i;
3836
}
@@ -42,9 +40,8 @@ module ts {
4240
}
4341

4442
export function filter<T>(array: T[], f: (x: T) => boolean): T[] {
45-
var result: T[];
4643
if (array) {
47-
result = [];
44+
var result: T[] = [];
4845
for (var i = 0, len = array.length; i < len; i++) {
4946
var item = array[i];
5047
if (f(item)) {
@@ -56,11 +53,9 @@ module ts {
5653
}
5754

5855
export function map<T, U>(array: T[], f: (x: T) => U): U[] {
59-
var result: U[];
6056
if (array) {
61-
result = [];
62-
var len = array.length;
63-
for (var i = 0; i < len; i++) {
57+
var result: U[] = [];
58+
for (var i = 0, len = array.length; i < len; i++) {
6459
result.push(f(array[i]));
6560
}
6661
}
@@ -73,6 +68,17 @@ module ts {
7368
return array1.concat(array2);
7469
}
7570

71+
export function uniqueElements<T>(array: T[]): T[] {
72+
if (array) {
73+
var result: T[] = [];
74+
for (var i = 0, len = array.length; i < len; i++) {
75+
var item = array[i];
76+
if (!contains(result, item)) result.push(item);
77+
}
78+
}
79+
return result;
80+
}
81+
7682
export function sum(array: any[], prop: string): number {
7783
var result = 0;
7884
for (var i = 0; i < array.length; i++) {

src/compiler/diagnosticInformationMap.generated.ts

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ module ts {
8484
An_object_literal_cannot_have_property_and_accessor_with_the_same_name: { code: 1119, category: DiagnosticCategory.Error, key: "An object literal cannot have property and accessor with the same name." },
8585
An_export_assignment_cannot_have_modifiers: { code: 1120, category: DiagnosticCategory.Error, key: "An export assignment cannot have modifiers." },
8686
Octal_literals_are_not_allowed_in_strict_mode: { code: 1121, category: DiagnosticCategory.Error, key: "Octal literals are not allowed in strict mode." },
87+
A_tuple_type_element_list_cannot_be_empty: { code: 1122, category: DiagnosticCategory.Error, key: "A tuple type element list cannot be empty." },
8788
Variable_declaration_list_cannot_be_empty: { code: 1123, category: DiagnosticCategory.Error, key: "Variable declaration list cannot be empty." },
8889
Digit_expected: { code: 1124, category: DiagnosticCategory.Error, key: "Digit expected." },
8990
Hexadecimal_digit_expected: { code: 1125, category: DiagnosticCategory.Error, key: "Hexadecimal digit expected." },

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@
327327
"category": "Error",
328328
"code": 1121
329329
},
330+
"A tuple type element list cannot be empty.": {
331+
"category": "Error",
332+
"code": 1122
333+
},
330334
"Variable declaration list cannot be empty.": {
331335
"category": "Error",
332336
"code": 1123

src/compiler/parser.ts

+20
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ module ts {
228228
return children((<TypeLiteralNode>node).members);
229229
case SyntaxKind.ArrayType:
230230
return child((<ArrayTypeNode>node).elementType);
231+
case SyntaxKind.TupleType:
232+
return children((<TupleTypeNode>node).elementTypes);
231233
case SyntaxKind.ArrayLiteral:
232234
return children((<ArrayLiteral>node).elements);
233235
case SyntaxKind.ObjectLiteral:
@@ -520,6 +522,7 @@ module ts {
520522
Parameters, // Parameters in parameter list
521523
TypeParameters, // Type parameters in type parameter list
522524
TypeArguments, // Type arguments in type argument list
525+
TupleElementTypes, // Element types in tuple element type list
523526
Count // Number of parsing contexts
524527
}
525528

@@ -547,6 +550,7 @@ module ts {
547550
case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected;
548551
case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected;
549552
case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected;
553+
case ParsingContext.TupleElementTypes: return Diagnostics.Type_expected;
550554
}
551555
};
552556

@@ -1015,6 +1019,7 @@ module ts {
10151019
case ParsingContext.Parameters:
10161020
return isParameter();
10171021
case ParsingContext.TypeArguments:
1022+
case ParsingContext.TupleElementTypes:
10181023
return isType();
10191024
}
10201025

@@ -1050,6 +1055,7 @@ module ts {
10501055
// Tokens other than ')' are here for better error recovery
10511056
return token === SyntaxKind.CloseParenToken || token === SyntaxKind.SemicolonToken;
10521057
case ParsingContext.ArrayLiteralMembers:
1058+
case ParsingContext.TupleElementTypes:
10531059
return token === SyntaxKind.CloseBracketToken;
10541060
case ParsingContext.Parameters:
10551061
// Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery
@@ -1570,6 +1576,17 @@ module ts {
15701576
return finishNode(node);
15711577
}
15721578

1579+
function parseTupleType(): TupleTypeNode {
1580+
var node = <TupleTypeNode>createNode(SyntaxKind.TupleType);
1581+
var startTokenPos = scanner.getTokenPos();
1582+
var startErrorCount = file.syntacticErrors.length;
1583+
node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
1584+
if (!node.elementTypes.length && file.syntacticErrors.length === startErrorCount) {
1585+
grammarErrorAtPos(startTokenPos, scanner.getStartPos() - startTokenPos, Diagnostics.A_tuple_type_element_list_cannot_be_empty);
1586+
}
1587+
return finishNode(node);
1588+
}
1589+
15731590
function parseFunctionType(signatureKind: SyntaxKind): TypeLiteralNode {
15741591
var node = <TypeLiteralNode>createNode(SyntaxKind.TypeLiteral);
15751592
var member = <SignatureDeclaration>createNode(signatureKind);
@@ -1600,6 +1617,8 @@ module ts {
16001617
return parseTypeQuery();
16011618
case SyntaxKind.OpenBraceToken:
16021619
return parseTypeLiteral();
1620+
case SyntaxKind.OpenBracketToken:
1621+
return parseTupleType();
16031622
case SyntaxKind.OpenParenToken:
16041623
case SyntaxKind.LessThanToken:
16051624
return parseFunctionType(SyntaxKind.CallSignature);
@@ -1623,6 +1642,7 @@ module ts {
16231642
case SyntaxKind.VoidKeyword:
16241643
case SyntaxKind.TypeOfKeyword:
16251644
case SyntaxKind.OpenBraceToken:
1645+
case SyntaxKind.OpenBracketToken:
16261646
case SyntaxKind.LessThanToken:
16271647
case SyntaxKind.NewKeyword:
16281648
return true;

src/compiler/types.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ module ts {
149149
TypeQuery,
150150
TypeLiteral,
151151
ArrayType,
152+
TupleType,
152153
// Expression
153154
ArrayLiteral,
154155
ObjectLiteral,
@@ -219,7 +220,7 @@ module ts {
219220
FirstFutureReservedWord = ImplementsKeyword,
220221
LastFutureReservedWord = YieldKeyword,
221222
FirstTypeNode = TypeReference,
222-
LastTypeNode = ArrayType,
223+
LastTypeNode = TupleType,
223224
FirstPunctuation = OpenBraceToken,
224225
LastPunctuation = CaretEqualsToken
225226
}
@@ -320,6 +321,10 @@ module ts {
320321
elementType: TypeNode;
321322
}
322323

324+
export interface TupleTypeNode extends TypeNode {
325+
elementTypes: NodeArray<TypeNode>;
326+
}
327+
323328
export interface StringLiteralTypeNode extends TypeNode {
324329
text: string;
325330
}
@@ -803,13 +808,14 @@ module ts {
803808
Class = 0x00000400, // Class
804809
Interface = 0x00000800, // Interface
805810
Reference = 0x00001000, // Generic type reference
806-
Anonymous = 0x00002000, // Anonymous
807-
FromSignature = 0x00004000, // Created for signature assignment check
811+
Tuple = 0x00002000, // Tuple
812+
Anonymous = 0x00004000, // Anonymous
813+
FromSignature = 0x00008000, // Created for signature assignment check
808814

809815
Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null,
810816
StringLike = String | StringLiteral,
811817
NumberLike = Number | Enum,
812-
ObjectType = Class | Interface | Reference | Anonymous
818+
ObjectType = Class | Interface | Reference | Tuple | Anonymous
813819
}
814820

815821
// Properties common to all types
@@ -862,6 +868,11 @@ module ts {
862868
openReferenceChecks: Map<boolean>; // Open type reference check cache
863869
}
864870

871+
export interface TupleType extends ObjectType {
872+
elementTypes: Type[]; // Element types
873+
baseArrayType: TypeReference; // Array<T> where T is best common type of element types
874+
}
875+
865876
// Resolved object type
866877
export interface ResolvedObjectType extends ObjectType {
867878
members: SymbolTable; // Properties by name
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
==== tests/cases/compiler/tupleTypes.ts (9 errors) ====
2+
var v1: []; // Error
3+
~~
4+
!!! A tuple type element list cannot be empty.
5+
var v2: [number];
6+
var v3: [number, string];
7+
var v4: [number, [string, string]];
8+
9+
var t: [number, string];
10+
var t0 = t[0]; // number
11+
var t0: number;
12+
var t1 = t[1]; // string
13+
var t1: string;
14+
var t2 = t[2]; // {}
15+
var t2: {};
16+
17+
t = []; // Error
18+
~
19+
!!! Type '{}[]' is not assignable to type '[number, string]':
20+
!!! Property '0' is missing in type '{}[]'.
21+
t = [1]; // Error
22+
~
23+
!!! Type '[number]' is not assignable to type '[number, string]':
24+
!!! Property '1' is missing in type '[number]'.
25+
t = [1, "hello"]; // Ok
26+
t = ["hello", 1]; // Error
27+
~
28+
!!! Type '[string, number]' is not assignable to type '[number, string]':
29+
!!! Types of property '0' are incompatible:
30+
!!! Type 'string' is not assignable to type 'number'.
31+
t = [1, "hello", 2]; // Ok
32+
33+
var tf: [string, (x: string) => number] = ["hello", x => x.length];
34+
35+
declare function ff<T, U>(a: T, b: [T, (x: T) => U]): U;
36+
var ff1 = ff("hello", ["foo", x => x.length]);
37+
var ff1: number;
38+
39+
function tuple2<T0, T1>(item0: T0, item1: T1): [T0, T1]{
40+
return [item0, item1];
41+
}
42+
43+
var tt = tuple2(1, "string");
44+
var tt0 = tt[0];
45+
var tt0: number;
46+
var tt1 = tt[1];
47+
var tt1: string;
48+
var tt2 = tt[2];
49+
var tt2: {};
50+
51+
tt = tuple2(1, undefined);
52+
tt = [1, undefined];
53+
tt = [undefined, undefined];
54+
tt = []; // Error
55+
~~
56+
!!! Type '{}[]' is not assignable to type '[number, string]'.
57+
58+
var a: number[];
59+
var a1: [number, string];
60+
var a2: [number, number];
61+
var a3: [number, {}];
62+
a = a1; // Error
63+
~
64+
!!! Type '[number, string]' is not assignable to type 'number[]':
65+
!!! Types of property 'pop' are incompatible:
66+
!!! Type '() => {}' is not assignable to type '() => number':
67+
!!! Type '{}' is not assignable to type 'number'.
68+
a = a2;
69+
a = a3; // Error
70+
~
71+
!!! Type '[number, {}]' is not assignable to type 'number[]':
72+
!!! Types of property 'pop' are incompatible:
73+
!!! Type '() => {}' is not assignable to type '() => number':
74+
!!! Type '{}' is not assignable to type 'number'.
75+
a1 = a2; // Error
76+
~~
77+
!!! Type '[number, number]' is not assignable to type '[number, string]':
78+
!!! Types of property '1' are incompatible:
79+
!!! Type 'number' is not assignable to type 'string'.
80+
a1 = a3; // Error
81+
~~
82+
!!! Type '[number, {}]' is not assignable to type '[number, string]':
83+
!!! Types of property '1' are incompatible:
84+
!!! Type '{}' is not assignable to type 'string'.
85+
a3 = a1;
86+
a3 = a2;
87+

tests/baselines/reference/typeName1.errors.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
==== tests/cases/compiler/typeName1.ts (16 errors) ====
1+
==== tests/cases/compiler/typeName1.ts (17 errors) ====
22
interface I {
33
k;
44
}
@@ -55,6 +55,8 @@
5555
~~~
5656
!!! Type 'number' is not assignable to type '{ z: I; x: boolean; y: (s: string) => boolean; w: { (): boolean; [x: string]: { x: any; y: any; }; [x: number]: { x: any; y: any; }; z: I; }; }[][]':
5757
!!! Property 'length' is missing in type 'Number'.
58+
~~~~
59+
!!! Property 'z' of type 'I' is not assignable to string index type '{ x: any; y: any; }'.
5860
var x13:{ new(): number; new(n:number):number; x: string; w: {y: number;}; (): {}; } = 3;
5961
~~~
6062
!!! Type 'number' is not assignable to type '{ (): {}; new (): number; new (n: number): number; x: string; w: { y: number; }; }':

tests/cases/compiler/tupleTypes.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var v1: []; // Error
2+
var v2: [number];
3+
var v3: [number, string];
4+
var v4: [number, [string, string]];
5+
6+
var t: [number, string];
7+
var t0 = t[0]; // number
8+
var t0: number;
9+
var t1 = t[1]; // string
10+
var t1: string;
11+
var t2 = t[2]; // {}
12+
var t2: {};
13+
14+
t = []; // Error
15+
t = [1]; // Error
16+
t = [1, "hello"]; // Ok
17+
t = ["hello", 1]; // Error
18+
t = [1, "hello", 2]; // Ok
19+
20+
var tf: [string, (x: string) => number] = ["hello", x => x.length];
21+
22+
declare function ff<T, U>(a: T, b: [T, (x: T) => U]): U;
23+
var ff1 = ff("hello", ["foo", x => x.length]);
24+
var ff1: number;
25+
26+
function tuple2<T0, T1>(item0: T0, item1: T1): [T0, T1]{
27+
return [item0, item1];
28+
}
29+
30+
var tt = tuple2(1, "string");
31+
var tt0 = tt[0];
32+
var tt0: number;
33+
var tt1 = tt[1];
34+
var tt1: string;
35+
var tt2 = tt[2];
36+
var tt2: {};
37+
38+
tt = tuple2(1, undefined);
39+
tt = [1, undefined];
40+
tt = [undefined, undefined];
41+
tt = []; // Error
42+
43+
var a: number[];
44+
var a1: [number, string];
45+
var a2: [number, number];
46+
var a3: [number, {}];
47+
a = a1; // Error
48+
a = a2;
49+
a = a3; // Error
50+
a1 = a2; // Error
51+
a1 = a3; // Error
52+
a3 = a1;
53+
a3 = a2;

0 commit comments

Comments
 (0)