Skip to content

Commit 9b17186

Browse files
authored
Leading and middle rest elements in tuple types (microsoft#41544)
* Support starting and middle rest elements in tuples * Accept new baselines * Include all rest arguments in error span * Accept new baselines * Fix tests * Add new tests * Fix lint errors
1 parent 72dfc58 commit 9b17186

24 files changed

+2339
-629
lines changed

src/compiler/checker.ts

+139-111
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

+26-6
Original file line numberDiff line numberDiff line change
@@ -843,10 +843,6 @@
843843
"category": "Error",
844844
"code": 1255
845845
},
846-
"A rest element must be last in a tuple type.": {
847-
"category": "Error",
848-
"code": 1256
849-
},
850846
"A required element cannot follow an optional element.": {
851847
"category": "Error",
852848
"code": 1257
@@ -875,6 +871,14 @@
875871
"category": "Error",
876872
"code": 1264
877873
},
874+
"A rest element cannot follow another rest element.": {
875+
"category": "Error",
876+
"code": 1265
877+
},
878+
"An optional element cannot follow a rest element.": {
879+
"category": "Error",
880+
"code": 1266
881+
},
878882

879883
"'with' statements are not allowed in an async function block.": {
880884
"category": "Error",
@@ -2621,9 +2625,25 @@
26212625
"category": "Error",
26222626
"code": 2621
26232627
},
2624-
"Element at index {0} is variadic in one type but not in the other.": {
2628+
"Source provides no match for required element at position {0} in target.": {
2629+
"category": "Error",
2630+
"code": 2623
2631+
},
2632+
"Source provides no match for variadic element at position {0} in target.": {
2633+
"category": "Error",
2634+
"code": 2624
2635+
},
2636+
"Variadic element at position {0} in source does not match element at position {1} in target.": {
2637+
"category": "Error",
2638+
"code": 2625
2639+
},
2640+
"Type at position {0} in source is not compatible with type at position {1} in target.": {
2641+
"category": "Error",
2642+
"code": 2626
2643+
},
2644+
"Type at positions {0} through {1} in source is not compatible with type at position {2} in target.": {
26252645
"category": "Error",
2626-
"code": 2622
2646+
"code": 2627
26272647
},
26282648

26292649
"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {

src/compiler/types.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -5251,18 +5251,21 @@ namespace ts {
52515251
}
52525252

52535253
export const enum ElementFlags {
5254-
Required = 1 << 0, // T
5255-
Optional = 1 << 1, // T?
5256-
Rest = 1 << 2, // ...T[]
5257-
Variadic = 1 << 3, // ...T
5258-
Variable = Rest | Variadic,
5254+
Required = 1 << 0, // T
5255+
Optional = 1 << 1, // T?
5256+
Rest = 1 << 2, // ...T[]
5257+
Variadic = 1 << 3, // ...T
5258+
Fixed = Required | Optional,
5259+
Variable = Rest | Variadic,
5260+
NonRequired = Optional | Rest | Variadic,
5261+
NonRest = Required | Optional | Variadic,
52595262
}
52605263

52615264
export interface TupleType extends GenericType {
52625265
elementFlags: readonly ElementFlags[];
5263-
minLength: number;
5264-
fixedLength: number;
5265-
hasRestElement: boolean;
5266+
minLength: number; // Number of required or variadic elements
5267+
fixedLength: number; // Number of initial required or optional elements
5268+
hasRestElement: boolean; // True if tuple has any rest or variadic elements
52665269
combinedFlags: ElementFlags;
52675270
readonly: boolean;
52685271
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];

tests/baselines/reference/api/tsserverlibrary.d.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2591,7 +2591,10 @@ declare namespace ts {
25912591
Optional = 2,
25922592
Rest = 4,
25932593
Variadic = 8,
2594-
Variable = 12
2594+
Fixed = 3,
2595+
Variable = 12,
2596+
NonRequired = 14,
2597+
NonRest = 11
25952598
}
25962599
export interface TupleType extends GenericType {
25972600
elementFlags: readonly ElementFlags[];

tests/baselines/reference/api/typescript.d.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2591,7 +2591,10 @@ declare namespace ts {
25912591
Optional = 2,
25922592
Rest = 4,
25932593
Variadic = 8,
2594-
Variable = 12
2594+
Fixed = 3,
2595+
Variable = 12,
2596+
NonRequired = 14,
2597+
NonRest = 11
25952598
}
25962599
export interface TupleType extends GenericType {
25972600
elementFlags: readonly ElementFlags[];

tests/baselines/reference/for-of39.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ tests/cases/conformance/es6/for-ofStatements/for-of39.ts(1,11): error TS2769: No
77
Type 'IteratorYieldResult<[string, number] | [string, true]>' is not assignable to type 'IteratorYieldResult<readonly [string, boolean]>'.
88
Type '[string, number] | [string, true]' is not assignable to type 'readonly [string, boolean]'.
99
Type '[string, number]' is not assignable to type 'readonly [string, boolean]'.
10-
Types of property '1' are incompatible.
10+
Type at position 1 in source is not compatible with type at position 1 in target.
1111
Type 'number' is not assignable to type 'boolean'.
1212
Overload 2 of 3, '(entries?: readonly (readonly [string, boolean])[]): Map<string, boolean>', gave the following error.
1313
Type 'number' is not assignable to type 'boolean'.
@@ -25,7 +25,7 @@ tests/cases/conformance/es6/for-ofStatements/for-of39.ts(1,11): error TS2769: No
2525
!!! error TS2769: Type 'IteratorYieldResult<[string, number] | [string, true]>' is not assignable to type 'IteratorYieldResult<readonly [string, boolean]>'.
2626
!!! error TS2769: Type '[string, number] | [string, true]' is not assignable to type 'readonly [string, boolean]'.
2727
!!! error TS2769: Type '[string, number]' is not assignable to type 'readonly [string, boolean]'.
28-
!!! error TS2769: Types of property '1' are incompatible.
28+
!!! error TS2769: Type at position 1 in source is not compatible with type at position 1 in target.
2929
!!! error TS2769: Type 'number' is not assignable to type 'boolean'.
3030
!!! error TS2769: Overload 2 of 3, '(entries?: readonly (readonly [string, boolean])[]): Map<string, boolean>', gave the following error.
3131
!!! error TS2769: Type 'number' is not assignable to type 'boolean'.

tests/baselines/reference/genericRestParameters2.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ f20(42, "hello", ...t3);
408408
>t3 : boolean[]
409409

410410
f20(42, "hello", ...t2, true);
411-
>f20(42, "hello", ...t2, true) : [number, string, string, ...boolean[]]
411+
>f20(42, "hello", ...t2, true) : [number, string, string, ...boolean[], boolean]
412412
>f20 : <T extends unknown[]>(...args: T) => T
413413
>42 : 42
414414
>"hello" : "hello"

tests/baselines/reference/genericRestParameters3.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ tests/cases/conformance/types/rest/genericRestParameters3.ts(59,5): error TS2345
114114

115115
let a = bar(10, 20);
116116
let b = bar<CoolArray<number>>(10, 20); // Error
117-
~~
117+
~~~~~~
118118
!!! error TS2345: Argument of type '[10, 20]' is not assignable to parameter of type 'CoolArray<number>'.
119119
!!! error TS2345: Property 'hello' is missing in type '[10, 20]' but required in type 'CoolArray<number>'.
120120
!!! related TS2728 tests/cases/conformance/types/rest/genericRestParameters3.ts:30:5: 'hello' is declared here.
@@ -133,7 +133,7 @@ tests/cases/conformance/types/rest/genericRestParameters3.ts(59,5): error TS2345
133133
!!! error TS2345: Property 'hello' is missing in type '[number]' but required in type 'CoolArray<unknown>'.
134134
!!! related TS2728 tests/cases/conformance/types/rest/genericRestParameters3.ts:30:5: 'hello' is declared here.
135135
baz(1, 2); // Error
136-
~
136+
~~~~
137137
!!! error TS2345: Argument of type '[number, number]' is not assignable to parameter of type 'CoolArray<unknown>'.
138138
!!! error TS2345: Property 'hello' is missing in type '[number, number]' but required in type 'CoolArray<unknown>'.
139139
!!! related TS2728 tests/cases/conformance/types/rest/genericRestParameters3.ts:30:5: 'hello' is declared here.

tests/baselines/reference/iterableArrayPattern28.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts(2,24): error
77
Type 'IteratorYieldResult<[string, number] | [string, boolean]>' is not assignable to type 'IteratorYieldResult<readonly [string, number]>'.
88
Type '[string, number] | [string, boolean]' is not assignable to type 'readonly [string, number]'.
99
Type '[string, boolean]' is not assignable to type 'readonly [string, number]'.
10-
Types of property '1' are incompatible.
10+
Type at position 1 in source is not compatible with type at position 1 in target.
1111
Type 'boolean' is not assignable to type 'number'.
1212
Overload 2 of 3, '(entries?: readonly (readonly [string, number])[]): Map<string, number>', gave the following error.
1313
Type 'boolean' is not assignable to type 'number'.
@@ -26,7 +26,7 @@ tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts(2,24): error
2626
!!! error TS2769: Type 'IteratorYieldResult<[string, number] | [string, boolean]>' is not assignable to type 'IteratorYieldResult<readonly [string, number]>'.
2727
!!! error TS2769: Type '[string, number] | [string, boolean]' is not assignable to type 'readonly [string, number]'.
2828
!!! error TS2769: Type '[string, boolean]' is not assignable to type 'readonly [string, number]'.
29-
!!! error TS2769: Types of property '1' are incompatible.
29+
!!! error TS2769: Type at position 1 in source is not compatible with type at position 1 in target.
3030
!!! error TS2769: Type 'boolean' is not assignable to type 'number'.
3131
!!! error TS2769: Overload 2 of 3, '(entries?: readonly (readonly [string, number])[]): Map<string, number>', gave the following error.
3232
!!! error TS2769: Type 'boolean' is not assignable to type 'number'.

tests/baselines/reference/optionalTupleElements1.errors.txt

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(11,29): error TS1257: A required element cannot follow an optional element.
22
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(15,5): error TS2322: Type 'T2' is not assignable to type 'T1'.
3-
Property '2' is optional in type '[number, string, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
3+
Source provides no match for required element at position 2 in target.
44
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(16,5): error TS2322: Type 'T3' is not assignable to type 'T1'.
5-
Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
5+
Source provides no match for required element at position 1 in target.
66
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(17,5): error TS2322: Type 'T4' is not assignable to type 'T1'.
7-
Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
7+
Source provides no match for required element at position 0 in target.
88
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(20,5): error TS2322: Type 'T3' is not assignable to type 'T2'.
9-
Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
9+
Source provides no match for required element at position 1 in target.
1010
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(21,5): error TS2322: Type 'T4' is not assignable to type 'T2'.
11-
Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
11+
Source provides no match for required element at position 0 in target.
1212
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(25,5): error TS2322: Type 'T4' is not assignable to type 'T3'.
13-
Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, (string | undefined)?, (boolean | undefined)?]'.
13+
Source provides no match for required element at position 0 in target.
1414

1515

1616
==== tests/cases/conformance/types/tuple/optionalTupleElements1.ts (7 errors) ====
@@ -33,32 +33,32 @@ tests/cases/conformance/types/tuple/optionalTupleElements1.ts(25,5): error TS232
3333
t1 = t2; // Error
3434
~~
3535
!!! error TS2322: Type 'T2' is not assignable to type 'T1'.
36-
!!! error TS2322: Property '2' is optional in type '[number, string, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
36+
!!! error TS2322: Source provides no match for required element at position 2 in target.
3737
t1 = t3; // Error
3838
~~
3939
!!! error TS2322: Type 'T3' is not assignable to type 'T1'.
40-
!!! error TS2322: Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
40+
!!! error TS2322: Source provides no match for required element at position 1 in target.
4141
t1 = t4; // Error
4242
~~
4343
!!! error TS2322: Type 'T4' is not assignable to type 'T1'.
44-
!!! error TS2322: Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
44+
!!! error TS2322: Source provides no match for required element at position 0 in target.
4545
t2 = t1;
4646
t2 = t2;
4747
t2 = t3; // Error
4848
~~
4949
!!! error TS2322: Type 'T3' is not assignable to type 'T2'.
50-
!!! error TS2322: Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
50+
!!! error TS2322: Source provides no match for required element at position 1 in target.
5151
t2 = t4; // Error
5252
~~
5353
!!! error TS2322: Type 'T4' is not assignable to type 'T2'.
54-
!!! error TS2322: Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
54+
!!! error TS2322: Source provides no match for required element at position 0 in target.
5555
t3 = t1;
5656
t3 = t2;
5757
t3 = t3;
5858
t3 = t4; // Error
5959
~~
6060
!!! error TS2322: Type 'T4' is not assignable to type 'T3'.
61-
!!! error TS2322: Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, (string | undefined)?, (boolean | undefined)?]'.
61+
!!! error TS2322: Source provides no match for required element at position 0 in target.
6262
t4 = t1;
6363
t4 = t2;
6464
t4 = t3;

tests/baselines/reference/restTupleElements1.errors.txt

+15-14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
tests/cases/conformance/types/tuple/restTupleElements1.ts(3,22): error TS1257: A required element cannot follow an optional element.
2-
tests/cases/conformance/types/tuple/restTupleElements1.ts(8,13): error TS1256: A rest element must be last in a tuple type.
32
tests/cases/conformance/types/tuple/restTupleElements1.ts(9,13): error TS2574: A rest element type must be an array type.
43
tests/cases/conformance/types/tuple/restTupleElements1.ts(10,13): error TS2574: A rest element type must be an array type.
54
tests/cases/conformance/types/tuple/restTupleElements1.ts(10,16): error TS8020: JSDoc types can only be used inside documentation comments.
65
tests/cases/conformance/types/tuple/restTupleElements1.ts(23,31): error TS2344: Type 'number[]' does not satisfy the constraint '[number, ...number[]]'.
7-
Property '0' is optional in type 'number[]' but required in type '[number, ...number[]]'.
6+
Source provides no match for required element at position 0 in target.
87
tests/cases/conformance/types/tuple/restTupleElements1.ts(24,31): error TS2344: Type '[]' does not satisfy the constraint '[number, ...number[]]'.
98
Source has 0 element(s) but target requires 1.
109
tests/cases/conformance/types/tuple/restTupleElements1.ts(29,18): error TS2344: Type 'number[]' does not satisfy the constraint '[number]'.
@@ -16,16 +15,18 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(32,31): error TS2344:
1615
tests/cases/conformance/types/tuple/restTupleElements1.ts(33,31): error TS2344: Type '[string, ...number[]]' does not satisfy the constraint '[number, ...number[]]'.
1716
Type 'string' is not assignable to type 'number'.
1817
tests/cases/conformance/types/tuple/restTupleElements1.ts(34,31): error TS2344: Type '[number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
19-
Types of property '2' are incompatible.
20-
Type 'string' is not assignable to type 'number'.
18+
Type at positions 1 through 2 in source is not compatible with type at position 1 in target.
19+
Type 'string | number' is not assignable to type 'number'.
20+
Type 'string' is not assignable to type 'number'.
2121
tests/cases/conformance/types/tuple/restTupleElements1.ts(35,31): error TS2344: Type '[number, number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
22-
Types of property '3' are incompatible.
23-
Type 'string' is not assignable to type 'number'.
22+
Type at positions 1 through 3 in source is not compatible with type at position 1 in target.
23+
Type 'string | number' is not assignable to type 'number'.
24+
Type 'string' is not assignable to type 'number'.
2425
tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: Argument of type '[]' is not assignable to parameter of type '[unknown, ...unknown[]]'.
2526
Source has 0 element(s) but target requires 1.
2627

2728

28-
==== tests/cases/conformance/types/tuple/restTupleElements1.ts (14 errors) ====
29+
==== tests/cases/conformance/types/tuple/restTupleElements1.ts (13 errors) ====
2930
type T00 = [string?];
3031
type T01 = [string, string?];
3132
type T02 = [string?, string]; // Error
@@ -36,8 +37,6 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: A
3637
type T05 = [...[...[...string[]]]];
3738
type T06 = [string, ...string[]];
3839
type T07 = [...string[], string]; // Error
39-
~~~~~~~~~~~
40-
!!! error TS1256: A rest element must be last in a tuple type.
4140
type T08 = [...string]; // Error
4241
~~~~~~~~~
4342
!!! error TS2574: A rest element type must be an array type.
@@ -61,7 +60,7 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: A
6160
assign<[number, ...number[]], number[]>(); // Error
6261
~~~~~~~~
6362
!!! error TS2344: Type 'number[]' does not satisfy the constraint '[number, ...number[]]'.
64-
!!! error TS2344: Property '0' is optional in type 'number[]' but required in type '[number, ...number[]]'.
63+
!!! error TS2344: Source provides no match for required element at position 0 in target.
6564
assign<[number, ...number[]], []>(); // Error
6665
~~
6766
!!! error TS2344: Type '[]' does not satisfy the constraint '[number, ...number[]]'.
@@ -90,13 +89,15 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: A
9089
assign<[number, ...number[]], [number, number, string]>(); // Error
9190
~~~~~~~~~~~~~~~~~~~~~~~~
9291
!!! error TS2344: Type '[number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
93-
!!! error TS2344: Types of property '2' are incompatible.
94-
!!! error TS2344: Type 'string' is not assignable to type 'number'.
92+
!!! error TS2344: Type at positions 1 through 2 in source is not compatible with type at position 1 in target.
93+
!!! error TS2344: Type 'string | number' is not assignable to type 'number'.
94+
!!! error TS2344: Type 'string' is not assignable to type 'number'.
9595
assign<[number, ...number[]], [number, number, number, string]>(); // Error
9696
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9797
!!! error TS2344: Type '[number, number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
98-
!!! error TS2344: Types of property '3' are incompatible.
99-
!!! error TS2344: Type 'string' is not assignable to type 'number'.
98+
!!! error TS2344: Type at positions 1 through 3 in source is not compatible with type at position 1 in target.
99+
!!! error TS2344: Type 'string | number' is not assignable to type 'number'.
100+
!!! error TS2344: Type 'string' is not assignable to type 'number'.
100101

101102
type T20 = [number, string, ...boolean[]];
102103

tests/baselines/reference/restTupleElements1.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ type T21 = T20[0];
107107
>T21 : number
108108

109109
type T22 = T20[0 | 1];
110-
>T22 : T22
110+
>T22 : string | number
111111

112112
type T23 = T20[0 | 1 | 2];
113113
>T23 : string | number | boolean

0 commit comments

Comments
 (0)