Skip to content

Commit

Permalink
Leading and middle rest elements in tuple types (#41544)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ahejlsberg authored Jan 5, 2021
1 parent 72dfc58 commit 9b17186
Show file tree
Hide file tree
Showing 24 changed files with 2,339 additions and 629 deletions.
250 changes: 139 additions & 111 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

32 changes: 26 additions & 6 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -843,10 +843,6 @@
"category": "Error",
"code": 1255
},
"A rest element must be last in a tuple type.": {
"category": "Error",
"code": 1256
},
"A required element cannot follow an optional element.": {
"category": "Error",
"code": 1257
Expand Down Expand Up @@ -875,6 +871,14 @@
"category": "Error",
"code": 1264
},
"A rest element cannot follow another rest element.": {
"category": "Error",
"code": 1265
},
"An optional element cannot follow a rest element.": {
"category": "Error",
"code": 1266
},

"'with' statements are not allowed in an async function block.": {
"category": "Error",
Expand Down Expand Up @@ -2621,9 +2625,25 @@
"category": "Error",
"code": 2621
},
"Element at index {0} is variadic in one type but not in the other.": {
"Source provides no match for required element at position {0} in target.": {
"category": "Error",
"code": 2623
},
"Source provides no match for variadic element at position {0} in target.": {
"category": "Error",
"code": 2624
},
"Variadic element at position {0} in source does not match element at position {1} in target.": {
"category": "Error",
"code": 2625
},
"Type at position {0} in source is not compatible with type at position {1} in target.": {
"category": "Error",
"code": 2626
},
"Type at positions {0} through {1} in source is not compatible with type at position {2} in target.": {
"category": "Error",
"code": 2622
"code": 2627
},

"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
Expand Down
19 changes: 11 additions & 8 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5251,18 +5251,21 @@ namespace ts {
}

export const enum ElementFlags {
Required = 1 << 0, // T
Optional = 1 << 1, // T?
Rest = 1 << 2, // ...T[]
Variadic = 1 << 3, // ...T
Variable = Rest | Variadic,
Required = 1 << 0, // T
Optional = 1 << 1, // T?
Rest = 1 << 2, // ...T[]
Variadic = 1 << 3, // ...T
Fixed = Required | Optional,
Variable = Rest | Variadic,
NonRequired = Optional | Rest | Variadic,
NonRest = Required | Optional | Variadic,
}

export interface TupleType extends GenericType {
elementFlags: readonly ElementFlags[];
minLength: number;
fixedLength: number;
hasRestElement: boolean;
minLength: number; // Number of required or variadic elements
fixedLength: number; // Number of initial required or optional elements
hasRestElement: boolean; // True if tuple has any rest or variadic elements
combinedFlags: ElementFlags;
readonly: boolean;
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
Expand Down
5 changes: 4 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2591,7 +2591,10 @@ declare namespace ts {
Optional = 2,
Rest = 4,
Variadic = 8,
Variable = 12
Fixed = 3,
Variable = 12,
NonRequired = 14,
NonRest = 11
}
export interface TupleType extends GenericType {
elementFlags: readonly ElementFlags[];
Expand Down
5 changes: 4 additions & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2591,7 +2591,10 @@ declare namespace ts {
Optional = 2,
Rest = 4,
Variadic = 8,
Variable = 12
Fixed = 3,
Variable = 12,
NonRequired = 14,
NonRest = 11
}
export interface TupleType extends GenericType {
elementFlags: readonly ElementFlags[];
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/for-of39.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ tests/cases/conformance/es6/for-ofStatements/for-of39.ts(1,11): error TS2769: No
Type 'IteratorYieldResult<[string, number] | [string, true]>' is not assignable to type 'IteratorYieldResult<readonly [string, boolean]>'.
Type '[string, number] | [string, true]' is not assignable to type 'readonly [string, boolean]'.
Type '[string, number]' is not assignable to type 'readonly [string, boolean]'.
Types of property '1' are incompatible.
Type at position 1 in source is not compatible with type at position 1 in target.
Type 'number' is not assignable to type 'boolean'.
Overload 2 of 3, '(entries?: readonly (readonly [string, boolean])[]): Map<string, boolean>', gave the following error.
Type 'number' is not assignable to type 'boolean'.
Expand All @@ -25,7 +25,7 @@ tests/cases/conformance/es6/for-ofStatements/for-of39.ts(1,11): error TS2769: No
!!! error TS2769: Type 'IteratorYieldResult<[string, number] | [string, true]>' is not assignable to type 'IteratorYieldResult<readonly [string, boolean]>'.
!!! error TS2769: Type '[string, number] | [string, true]' is not assignable to type 'readonly [string, boolean]'.
!!! error TS2769: Type '[string, number]' is not assignable to type 'readonly [string, boolean]'.
!!! error TS2769: Types of property '1' are incompatible.
!!! error TS2769: Type at position 1 in source is not compatible with type at position 1 in target.
!!! error TS2769: Type 'number' is not assignable to type 'boolean'.
!!! error TS2769: Overload 2 of 3, '(entries?: readonly (readonly [string, boolean])[]): Map<string, boolean>', gave the following error.
!!! error TS2769: Type 'number' is not assignable to type 'boolean'.
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/genericRestParameters2.types
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ f20(42, "hello", ...t3);
>t3 : boolean[]

f20(42, "hello", ...t2, true);
>f20(42, "hello", ...t2, true) : [number, string, string, ...boolean[]]
>f20(42, "hello", ...t2, true) : [number, string, string, ...boolean[], boolean]
>f20 : <T extends unknown[]>(...args: T) => T
>42 : 42
>"hello" : "hello"
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/genericRestParameters3.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ tests/cases/conformance/types/rest/genericRestParameters3.ts(59,5): error TS2345

let a = bar(10, 20);
let b = bar<CoolArray<number>>(10, 20); // Error
~~
~~~~~~
!!! error TS2345: Argument of type '[10, 20]' is not assignable to parameter of type 'CoolArray<number>'.
!!! error TS2345: Property 'hello' is missing in type '[10, 20]' but required in type 'CoolArray<number>'.
!!! related TS2728 tests/cases/conformance/types/rest/genericRestParameters3.ts:30:5: 'hello' is declared here.
Expand All @@ -133,7 +133,7 @@ tests/cases/conformance/types/rest/genericRestParameters3.ts(59,5): error TS2345
!!! error TS2345: Property 'hello' is missing in type '[number]' but required in type 'CoolArray<unknown>'.
!!! related TS2728 tests/cases/conformance/types/rest/genericRestParameters3.ts:30:5: 'hello' is declared here.
baz(1, 2); // Error
~
~~~~
!!! error TS2345: Argument of type '[number, number]' is not assignable to parameter of type 'CoolArray<unknown>'.
!!! error TS2345: Property 'hello' is missing in type '[number, number]' but required in type 'CoolArray<unknown>'.
!!! related TS2728 tests/cases/conformance/types/rest/genericRestParameters3.ts:30:5: 'hello' is declared here.
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/iterableArrayPattern28.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts(2,24): error
Type 'IteratorYieldResult<[string, number] | [string, boolean]>' is not assignable to type 'IteratorYieldResult<readonly [string, number]>'.
Type '[string, number] | [string, boolean]' is not assignable to type 'readonly [string, number]'.
Type '[string, boolean]' is not assignable to type 'readonly [string, number]'.
Types of property '1' are incompatible.
Type at position 1 in source is not compatible with type at position 1 in target.
Type 'boolean' is not assignable to type 'number'.
Overload 2 of 3, '(entries?: readonly (readonly [string, number])[]): Map<string, number>', gave the following error.
Type 'boolean' is not assignable to type 'number'.
Expand All @@ -26,7 +26,7 @@ tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts(2,24): error
!!! error TS2769: Type 'IteratorYieldResult<[string, number] | [string, boolean]>' is not assignable to type 'IteratorYieldResult<readonly [string, number]>'.
!!! error TS2769: Type '[string, number] | [string, boolean]' is not assignable to type 'readonly [string, number]'.
!!! error TS2769: Type '[string, boolean]' is not assignable to type 'readonly [string, number]'.
!!! error TS2769: Types of property '1' are incompatible.
!!! error TS2769: Type at position 1 in source is not compatible with type at position 1 in target.
!!! error TS2769: Type 'boolean' is not assignable to type 'number'.
!!! error TS2769: Overload 2 of 3, '(entries?: readonly (readonly [string, number])[]): Map<string, number>', gave the following error.
!!! error TS2769: Type 'boolean' is not assignable to type 'number'.
24 changes: 12 additions & 12 deletions tests/baselines/reference/optionalTupleElements1.errors.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(11,29): error TS1257: A required element cannot follow an optional element.
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(15,5): error TS2322: Type 'T2' is not assignable to type 'T1'.
Property '2' is optional in type '[number, string, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
Source provides no match for required element at position 2 in target.
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(16,5): error TS2322: Type 'T3' is not assignable to type 'T1'.
Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
Source provides no match for required element at position 1 in target.
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(17,5): error TS2322: Type 'T4' is not assignable to type 'T1'.
Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
Source provides no match for required element at position 0 in target.
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(20,5): error TS2322: Type 'T3' is not assignable to type 'T2'.
Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
Source provides no match for required element at position 1 in target.
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(21,5): error TS2322: Type 'T4' is not assignable to type 'T2'.
Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
Source provides no match for required element at position 0 in target.
tests/cases/conformance/types/tuple/optionalTupleElements1.ts(25,5): error TS2322: Type 'T4' is not assignable to type 'T3'.
Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, (string | undefined)?, (boolean | undefined)?]'.
Source provides no match for required element at position 0 in target.


==== tests/cases/conformance/types/tuple/optionalTupleElements1.ts (7 errors) ====
Expand All @@ -33,32 +33,32 @@ tests/cases/conformance/types/tuple/optionalTupleElements1.ts(25,5): error TS232
t1 = t2; // Error
~~
!!! error TS2322: Type 'T2' is not assignable to type 'T1'.
!!! error TS2322: Property '2' is optional in type '[number, string, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
!!! error TS2322: Source provides no match for required element at position 2 in target.
t1 = t3; // Error
~~
!!! error TS2322: Type 'T3' is not assignable to type 'T1'.
!!! error TS2322: Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
!!! error TS2322: Source provides no match for required element at position 1 in target.
t1 = t4; // Error
~~
!!! error TS2322: Type 'T4' is not assignable to type 'T1'.
!!! error TS2322: Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, boolean]'.
!!! error TS2322: Source provides no match for required element at position 0 in target.
t2 = t1;
t2 = t2;
t2 = t3; // Error
~~
!!! error TS2322: Type 'T3' is not assignable to type 'T2'.
!!! error TS2322: Property '1' is optional in type '[number, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
!!! error TS2322: Source provides no match for required element at position 1 in target.
t2 = t4; // Error
~~
!!! error TS2322: Type 'T4' is not assignable to type 'T2'.
!!! error TS2322: Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, string, (boolean | undefined)?]'.
!!! error TS2322: Source provides no match for required element at position 0 in target.
t3 = t1;
t3 = t2;
t3 = t3;
t3 = t4; // Error
~~
!!! error TS2322: Type 'T4' is not assignable to type 'T3'.
!!! error TS2322: Property '0' is optional in type '[(number | undefined)?, (string | undefined)?, (boolean | undefined)?]' but required in type '[number, (string | undefined)?, (boolean | undefined)?]'.
!!! error TS2322: Source provides no match for required element at position 0 in target.
t4 = t1;
t4 = t2;
t4 = t3;
Expand Down
29 changes: 15 additions & 14 deletions tests/baselines/reference/restTupleElements1.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
tests/cases/conformance/types/tuple/restTupleElements1.ts(3,22): error TS1257: A required element cannot follow an optional element.
tests/cases/conformance/types/tuple/restTupleElements1.ts(8,13): error TS1256: A rest element must be last in a tuple type.
tests/cases/conformance/types/tuple/restTupleElements1.ts(9,13): error TS2574: A rest element type must be an array type.
tests/cases/conformance/types/tuple/restTupleElements1.ts(10,13): error TS2574: A rest element type must be an array type.
tests/cases/conformance/types/tuple/restTupleElements1.ts(10,16): error TS8020: JSDoc types can only be used inside documentation comments.
tests/cases/conformance/types/tuple/restTupleElements1.ts(23,31): error TS2344: Type 'number[]' does not satisfy the constraint '[number, ...number[]]'.
Property '0' is optional in type 'number[]' but required in type '[number, ...number[]]'.
Source provides no match for required element at position 0 in target.
tests/cases/conformance/types/tuple/restTupleElements1.ts(24,31): error TS2344: Type '[]' does not satisfy the constraint '[number, ...number[]]'.
Source has 0 element(s) but target requires 1.
tests/cases/conformance/types/tuple/restTupleElements1.ts(29,18): error TS2344: Type 'number[]' does not satisfy the constraint '[number]'.
Expand All @@ -16,16 +15,18 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(32,31): error TS2344:
tests/cases/conformance/types/tuple/restTupleElements1.ts(33,31): error TS2344: Type '[string, ...number[]]' does not satisfy the constraint '[number, ...number[]]'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/tuple/restTupleElements1.ts(34,31): error TS2344: Type '[number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
Types of property '2' are incompatible.
Type 'string' is not assignable to type 'number'.
Type at positions 1 through 2 in source is not compatible with type at position 1 in target.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/tuple/restTupleElements1.ts(35,31): error TS2344: Type '[number, number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
Types of property '3' are incompatible.
Type 'string' is not assignable to type 'number'.
Type at positions 1 through 3 in source is not compatible with type at position 1 in target.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: Argument of type '[]' is not assignable to parameter of type '[unknown, ...unknown[]]'.
Source has 0 element(s) but target requires 1.


==== tests/cases/conformance/types/tuple/restTupleElements1.ts (14 errors) ====
==== tests/cases/conformance/types/tuple/restTupleElements1.ts (13 errors) ====
type T00 = [string?];
type T01 = [string, string?];
type T02 = [string?, string]; // Error
Expand All @@ -36,8 +37,6 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: A
type T05 = [...[...[...string[]]]];
type T06 = [string, ...string[]];
type T07 = [...string[], string]; // Error
~~~~~~~~~~~
!!! error TS1256: A rest element must be last in a tuple type.
type T08 = [...string]; // Error
~~~~~~~~~
!!! error TS2574: A rest element type must be an array type.
Expand All @@ -61,7 +60,7 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: A
assign<[number, ...number[]], number[]>(); // Error
~~~~~~~~
!!! error TS2344: Type 'number[]' does not satisfy the constraint '[number, ...number[]]'.
!!! error TS2344: Property '0' is optional in type 'number[]' but required in type '[number, ...number[]]'.
!!! error TS2344: Source provides no match for required element at position 0 in target.
assign<[number, ...number[]], []>(); // Error
~~
!!! error TS2344: Type '[]' does not satisfy the constraint '[number, ...number[]]'.
Expand Down Expand Up @@ -90,13 +89,15 @@ tests/cases/conformance/types/tuple/restTupleElements1.ts(59,4): error TS2345: A
assign<[number, ...number[]], [number, number, string]>(); // Error
~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2344: Type '[number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
!!! error TS2344: Types of property '2' are incompatible.
!!! error TS2344: Type 'string' is not assignable to type 'number'.
!!! error TS2344: Type at positions 1 through 2 in source is not compatible with type at position 1 in target.
!!! error TS2344: Type 'string | number' is not assignable to type 'number'.
!!! error TS2344: Type 'string' is not assignable to type 'number'.
assign<[number, ...number[]], [number, number, number, string]>(); // Error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2344: Type '[number, number, number, string]' does not satisfy the constraint '[number, ...number[]]'.
!!! error TS2344: Types of property '3' are incompatible.
!!! error TS2344: Type 'string' is not assignable to type 'number'.
!!! error TS2344: Type at positions 1 through 3 in source is not compatible with type at position 1 in target.
!!! error TS2344: Type 'string | number' is not assignable to type 'number'.
!!! error TS2344: Type 'string' is not assignable to type 'number'.

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

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/restTupleElements1.types
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ type T21 = T20[0];
>T21 : number

type T22 = T20[0 | 1];
>T22 : T22
>T22 : string | number

type T23 = T20[0 | 1 | 2];
>T23 : string | number | boolean
Expand Down
Loading

0 comments on commit 9b17186

Please sign in to comment.