From fa94d96ddde75a5be7b75da2f43ad818727a4ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 21 Aug 2023 12:08:52 +0200 Subject: [PATCH] Allow rest elements to follow other rest elements in tuple types --- src/compiler/checker.ts | 23 +++++++++++++++---- src/compiler/diagnosticMessages.json | 4 ---- .../reference/variadicTuples2.errors.txt | 11 ++++----- tests/baselines/reference/variadicTuples2.js | 12 +++++----- .../reference/variadicTuples2.symbols | 12 +++++----- .../baselines/reference/variadicTuples2.types | 12 +++++----- .../types/tuple/variadicTuples2.ts | 6 ++--- 7 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9c6b8237a7f74..5f072d06cd0c7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16307,6 +16307,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } + function isTupleNormalizable(node: TupleTypeNode) { + let hasSeenRest = false; + for (const element of node.elements) { + const flags = getTupleElementFlags(element); + if (flags & ElementFlags.Variadic) { + return true; + } + if (flags & ElementFlags.Rest) { + if (hasSeenRest) { + return true; + } + hasSeenRest = true; + } + } + return false; + } + function getTypeFromArrayOrTupleTypeNode(node: ArrayTypeNode | TupleTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { @@ -16314,7 +16331,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (target === emptyGenericType) { links.resolvedType = emptyObjectType; } - else if (!(node.kind === SyntaxKind.TupleType && some(node.elements, e => !!(getTupleElementFlags(e) & ElementFlags.Variadic))) && isDeferredTypeReferenceNode(node)) { + else if (!(node.kind === SyntaxKind.TupleType && isTupleNormalizable(node)) && isDeferredTypeReferenceNode(node)) { links.resolvedType = node.kind === SyntaxKind.TupleType && node.elements.length === 0 ? target : createDeferredTypeReference(target, node, /*mapper*/ undefined); } @@ -39410,10 +39427,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } else if (flags & ElementFlags.Rest) { - if (seenRestElement) { - grammarErrorOnNode(e, Diagnostics.A_rest_element_cannot_follow_another_rest_element); - break; - } seenRestElement = true; } else if (flags & ElementFlags.Optional) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 117f52a8e2c84..1f87b9b351980 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -855,10 +855,6 @@ "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 diff --git a/tests/baselines/reference/variadicTuples2.errors.txt b/tests/baselines/reference/variadicTuples2.errors.txt index 4539265d4bff6..357cd6a4d8232 100644 --- a/tests/baselines/reference/variadicTuples2.errors.txt +++ b/tests/baselines/reference/variadicTuples2.errors.txt @@ -1,4 +1,3 @@ -variadicTuples2.ts(7,34): error TS1265: A rest element cannot follow another rest element. variadicTuples2.ts(8,34): error TS1266: An optional element cannot follow a rest element. variadicTuples2.ts(9,30): error TS1257: A required element cannot follow an optional element. variadicTuples2.ts(42,1): error TS2322: Type '[string, string, number, number]' is not assignable to type '[...string[], number]'. @@ -65,20 +64,18 @@ variadicTuples2.ts(134,25): error TS2345: Argument of type '["blah2", 1, 2, 3]' Type 'number' is not assignable to type 'string'. -==== variadicTuples2.ts (29 errors) ==== +==== variadicTuples2.ts (28 errors) ==== // Declarations type V00 = [number, ...string[]]; type V01 = [...string[], number]; type V03 = [number, ...string[], number]; + type V04 = [number, ...string[], ...boolean[]]; - type V10 = [number, ...string[], ...boolean[]]; // Error - ~~~~~~~~~~~~ -!!! error TS1265: A rest element cannot follow another rest element. - type V11 = [number, ...string[], boolean?]; // Error + type V10 = [number, ...string[], boolean?]; // Error ~~~~~~~~ !!! error TS1266: An optional element cannot follow a rest element. - type V12 = [number, string?, boolean]; // Error + type V11 = [number, string?, boolean]; // Error ~~~~~~~ !!! error TS1257: A required element cannot follow an optional element. diff --git a/tests/baselines/reference/variadicTuples2.js b/tests/baselines/reference/variadicTuples2.js index 8b0d2b25a180a..bbb5c65634aed 100644 --- a/tests/baselines/reference/variadicTuples2.js +++ b/tests/baselines/reference/variadicTuples2.js @@ -6,10 +6,10 @@ type V00 = [number, ...string[]]; type V01 = [...string[], number]; type V03 = [number, ...string[], number]; +type V04 = [number, ...string[], ...boolean[]]; -type V10 = [number, ...string[], ...boolean[]]; // Error -type V11 = [number, ...string[], boolean?]; // Error -type V12 = [number, string?, boolean]; // Error +type V10 = [number, ...string[], boolean?]; // Error +type V11 = [number, string?, boolean]; // Error // Normalization @@ -239,9 +239,9 @@ var e1 = foo('blah1', 'blah2', 1, 2, 3); // Error type V00 = [number, ...string[]]; type V01 = [...string[], number]; type V03 = [number, ...string[], number]; -type V10 = [number, ...string[], ...boolean[]]; -type V11 = [number, ...string[], boolean?]; -type V12 = [number, string?, boolean]; +type V04 = [number, ...string[], ...boolean[]]; +type V10 = [number, ...string[], boolean?]; +type V11 = [number, string?, boolean]; type Tup3 = [...T, ...U, ...V]; type V20 = Tup3<[number], string[], [number]>; type V21 = Tup3<[number], [string?], [boolean]>; diff --git a/tests/baselines/reference/variadicTuples2.symbols b/tests/baselines/reference/variadicTuples2.symbols index 852bdf904f8c9..37a32c3191a71 100644 --- a/tests/baselines/reference/variadicTuples2.symbols +++ b/tests/baselines/reference/variadicTuples2.symbols @@ -12,14 +12,14 @@ type V01 = [...string[], number]; type V03 = [number, ...string[], number]; >V03 : Symbol(V03, Decl(variadicTuples2.ts, 3, 33)) -type V10 = [number, ...string[], ...boolean[]]; // Error ->V10 : Symbol(V10, Decl(variadicTuples2.ts, 4, 41)) +type V04 = [number, ...string[], ...boolean[]]; +>V04 : Symbol(V04, Decl(variadicTuples2.ts, 4, 41)) -type V11 = [number, ...string[], boolean?]; // Error ->V11 : Symbol(V11, Decl(variadicTuples2.ts, 6, 47)) +type V10 = [number, ...string[], boolean?]; // Error +>V10 : Symbol(V10, Decl(variadicTuples2.ts, 5, 47)) -type V12 = [number, string?, boolean]; // Error ->V12 : Symbol(V12, Decl(variadicTuples2.ts, 7, 43)) +type V11 = [number, string?, boolean]; // Error +>V11 : Symbol(V11, Decl(variadicTuples2.ts, 7, 43)) // Normalization diff --git a/tests/baselines/reference/variadicTuples2.types b/tests/baselines/reference/variadicTuples2.types index d80650c231e24..ee8d7cd587fb9 100644 --- a/tests/baselines/reference/variadicTuples2.types +++ b/tests/baselines/reference/variadicTuples2.types @@ -12,14 +12,14 @@ type V01 = [...string[], number]; type V03 = [number, ...string[], number]; >V03 : [number, ...string[], number] -type V10 = [number, ...string[], ...boolean[]]; // Error ->V10 : [number, ...string[], ...boolean[]] +type V04 = [number, ...string[], ...boolean[]]; +>V04 : [number, ...(string | boolean)[]] -type V11 = [number, ...string[], boolean?]; // Error ->V11 : [number, ...string[], (boolean | undefined)?] +type V10 = [number, ...string[], boolean?]; // Error +>V10 : [number, ...string[], (boolean | undefined)?] -type V12 = [number, string?, boolean]; // Error ->V12 : [number, (string | undefined)?, boolean] +type V11 = [number, string?, boolean]; // Error +>V11 : [number, (string | undefined)?, boolean] // Normalization diff --git a/tests/cases/conformance/types/tuple/variadicTuples2.ts b/tests/cases/conformance/types/tuple/variadicTuples2.ts index 8ad04d44904b1..6a6c1bfb2f1d0 100644 --- a/tests/cases/conformance/types/tuple/variadicTuples2.ts +++ b/tests/cases/conformance/types/tuple/variadicTuples2.ts @@ -6,10 +6,10 @@ type V00 = [number, ...string[]]; type V01 = [...string[], number]; type V03 = [number, ...string[], number]; +type V04 = [number, ...string[], ...boolean[]]; -type V10 = [number, ...string[], ...boolean[]]; // Error -type V11 = [number, ...string[], boolean?]; // Error -type V12 = [number, string?, boolean]; // Error +type V10 = [number, ...string[], boolean?]; // Error +type V11 = [number, string?, boolean]; // Error // Normalization