fix(parser): remove type-dependent tuple rest diagnostics (#27234)

TS1265 and TS1266 depend on the resolved types of tuple rest elements. The parser cannot reliably distinguish concrete arrays from tuple aliases or variadic type parameters. Its spelling-based `Array` check rejects valid code such as:

```ts
// Array shadows the built-in name and is a variadic type parameter.
// Both are valid, but the parser incorrectly reported TS1265 / TS1266.
type X<Array extends unknown[]> = [...Array, ...Array];
type Y<Array extends unknown[]> = [...Array, number?];
```

```ts
type Pair = [string, number];
type Items = string[];

// Valid: spreading Pair contributes two fixed elements, then an optional one.
type Fixed = [...Pair, boolean?];

// Invalid: Items is an unbounded array rest, but the parser cannot resolve it.
type OptionalAfterArray = [...Items, boolean?]; // TS1266
// Invalid: two unbounded array rests; also requires resolving the alias.
type TwoArrays = [...Items, ...Items]; // TS1265
```

```ts
// Invalid: the unconstrained type parameter might not be an array.
// TypeScript reports TS2574, but the parser incorrectly reported TS1265.
type Unconstrained<Array> = [...Array, ...Array];

// Invalid: the built-in Array requires a type argument.
// TypeScript reports TS2314, but the parser incorrectly reported TS1265.
type MissingArguments = [...Array, ...Array];
```

Remove both diagnostics and their parser checks rather than retaining partial syntax-based reporting. Resolving aliases, checking generic constraints, and validating rest types belong in a type checker, which can also select the correct diagnostic for invalid rests or missing type arguments.
This commit is contained in:
camc314 committed 2026-09-30 20:40:58 +00:00
1 parent 7c61ebe1be
commit d0b9372b8b
6 files changed
+3 -154

No files matched your search

-14
View File
@@ -428,20 +428,6 @@ parser_diagnostics! {
])
};
// A rest element cannot follow another rest element. ts(1265)
rest_element_cannot_follow_another_rest_element(seen_span: Span, span: Span) => {
ts_error("1265", "A rest element cannot follow another rest element.")
.with_labels([span.label("Second rest element here"), seen_span.label("First seen here")])
};
// An optional element cannot follow a rest element. ts(1266)
optional_element_cannot_follow_rest_element(span: Span, rest_span: Span) => {
ts_error("1266", "An optional element cannot follow a rest element.").with_labels([
span.label("Optional element here"),
rest_span.label("Rest element seen here"),
])
};
// A type-only import can specify a default import or named bindings, but not both. ts(1363)
type_only_import_default_and_named(specifier_span: Span) => {
ts_error(
-35
View File
@@ -969,39 +969,10 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let opening_span = self.cur_token().span();
self.expect(Kind::LBrack);
let mut seen_rest_span: Option<Span> = None;
let mut seen_optional_span: Option<Span> = None;
let (elements, _) =
self.parse_delimited_list(Kind::RBrack, Kind::Comma, opening_span, |me| {
let tuple = me.parse_tuple_element();
// check for array type, because unknown types can be destructed, example of valid code:
// type C<T extends unknown[]> = [...string[], ...T];
// example of invalid code:
// type C<T extends unknown[]> = [...string[], ...T[]];
if let TSTupleElement::TSRestType(rest) = &tuple
&& let Some(rest_type) = (match &rest.type_annotation {
TSType::TSNamedTupleMember(named) => named.element_type.as_ts_type(),
ty => Some(ty),
})
&& match rest_type {
TSType::TSArrayType(_) => true,
// Check for `Array<...>` type
TSType::TSTypeReference(ts_ref) => match &ts_ref.type_name {
TSTypeName::IdentifierReference(id_ref) => id_ref.name == "Array",
_ => false,
},
_ => false,
}
{
if let Some(seen_span) = seen_rest_span {
me.error(diagnostics::rest_element_cannot_follow_another_rest_element(
seen_span,
tuple.span(),
));
}
seen_rest_span = Some(tuple.span());
}
if !match &tuple {
TSTupleElement::TSOptionalType(_) | TSTupleElement::TSRestType(_) => true,
TSTupleElement::TSNamedTupleMember(named) => named.optional,
@@ -1019,12 +990,6 @@ impl<'a, C: Config> ParserImpl<'a, C> {
TSTupleElement::TSNamedTupleMember(named) => named.optional,
_ => false,
} {
if let Some(seen_rest_span) = seen_rest_span {
me.error(diagnostics::optional_element_cannot_follow_rest_element(
tuple.span(),
seen_rest_span,
));
}
seen_optional_span = Some(tuple.span());
}
@@ -1,4 +0,0 @@
type OptionalAfterRest = [...a: string[], b?: number];
type RestAfterRest = [...a: string[], ...b: number[]];
type NamedRestAfterUnnamedRest = [...string[], ...b: number[]];
type UnnamedRestAfterNamedRest = [...a: string[], ...number[]];
+2 -2
View File
@@ -1,3 +1,3 @@
lexer_misc Summary:
AST Parsed : 354/354 (100.00%)
Positive Passed: 354/354 (100.00%)
AST Parsed : 353/353 (100.00%)
Positive Passed: 353/353 (100.00%)
+1 -39
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 112/112 (100.00%)
Positive Passed: 112/112 (100.00%)
Negative Passed: 244/244 (100.00%)
Negative Passed: 243/243 (100.00%)
× TS(1275): 'accessor' modifier can only appear on a property declaration.
╭─[misc/fail/accessor-modifier-abstract-method.ts:1:29]
@@ -5142,44 +5142,6 @@ Negative Passed: 244/244 (100.00%)
╰────
help: Try inserting a semicolon here
× TS(1266): An optional element cannot follow a rest element.
╭─[misc/fail/tuple-named-rest.ts:1:27]
1 │ type OptionalAfterRest = [...a: string[], b?: number];
· ───────┬────── ─────┬────
· │ ╰── Optional element here
· ╰── Rest element seen here
2 │ type RestAfterRest = [...a: string[], ...b: number[]];
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[misc/fail/tuple-named-rest.ts:2:23]
1 │ type OptionalAfterRest = [...a: string[], b?: number];
2 │ type RestAfterRest = [...a: string[], ...b: number[]];
· ───────┬────── ───────┬──────
· │ ╰── Second rest element here
· ╰── First seen here
3 │ type NamedRestAfterUnnamedRest = [...string[], ...b: number[]];
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[misc/fail/tuple-named-rest.ts:3:35]
2 │ type RestAfterRest = [...a: string[], ...b: number[]];
3 │ type NamedRestAfterUnnamedRest = [...string[], ...b: number[]];
· ─────┬───── ───────┬──────
· │ ╰── Second rest element here
· ╰── First seen here
4 │ type UnnamedRestAfterNamedRest = [...a: string[], ...number[]];
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[misc/fail/tuple-named-rest.ts:4:35]
3 │ type NamedRestAfterUnnamedRest = [...string[], ...b: number[]];
4 │ type UnnamedRestAfterNamedRest = [...a: string[], ...number[]];
· ───────┬────── ─────┬─────
· │ ╰── Second rest element here
· ╰── First seen here
╰────
× TS(8011): Type arguments can only be used in TypeScript files.
╭─[misc/fail/type-arguments-in-js.js:1:18]
1 │ class A extends B<C> {}
@@ -32064,26 +32064,6 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
4 │ type T03 = [...string[]];
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[typescript/tsc/testdata/tests/cases/conformance/types/tuple/variadicTuples2.ts:7:21]
6 │
7 │ type V10 = [number, ...string[], ...boolean[]]; // Error
· ─────┬───── ──────┬─────
· │ ╰── Second rest element here
· ╰── First seen here
8 │ type V11 = [number, ...string[], boolean?]; // Error
╰────
× TS(1266): An optional element cannot follow a rest element.
╭─[typescript/tsc/testdata/tests/cases/conformance/types/tuple/variadicTuples2.ts:8:21]
7 │ type V10 = [number, ...string[], ...boolean[]]; // Error
8 │ type V11 = [number, ...string[], boolean?]; // Error
· ─────┬───── ────┬───
· │ ╰── Optional element here
· ╰── Rest element seen here
9 │ type V12 = [number, string?, boolean]; // Error
╰────
× TS(1257): A required element cannot follow an optional element.
╭─[typescript/tsc/testdata/tests/cases/conformance/types/tuple/variadicTuples2.ts:9:21]
8 │ type V11 = [number, ...string[], boolean?]; // Error
@@ -32094,46 +32074,6 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
10 │
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[typescript/tsc/testdata/tests/cases/conformance/types/tuple/variadicTuples2.ts:11:13]
10 │
11 │ type V15 = [...string[], ...number[]]; // Error
· ─────┬───── ─────┬─────
· │ ╰── Second rest element here
· ╰── First seen here
12 │ type V16 = [...string[], ...Array<number>]; // Error
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[typescript/tsc/testdata/tests/cases/conformance/types/tuple/variadicTuples2.ts:12:13]
11 │ type V15 = [...string[], ...number[]]; // Error
12 │ type V16 = [...string[], ...Array<number>]; // Error
· ─────┬───── ────────┬───────
· │ ╰── Second rest element here
· ╰── First seen here
13 │ type V17 = [...Array<string>, ...number[]]; // Error
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[typescript/tsc/testdata/tests/cases/conformance/types/tuple/variadicTuples2.ts:13:13]
12 │ type V16 = [...string[], ...Array<number>]; // Error
13 │ type V17 = [...Array<string>, ...number[]]; // Error
· ────────┬─────── ─────┬─────
· │ ╰── Second rest element here
· ╰── First seen here
14 │ type V18 = [...Array<string>, ...Array<number>]; // Error
╰────
× TS(1265): A rest element cannot follow another rest element.
╭─[typescript/tsc/testdata/tests/cases/conformance/types/tuple/variadicTuples2.ts:14:13]
13 │ type V17 = [...Array<string>, ...number[]]; // Error
14 │ type V18 = [...Array<string>, ...Array<number>]; // Error
· ────────┬─────── ────────┬───────
· │ ╰── Second rest element here
· ╰── First seen here
15 │
╰────
× TS(2457): Type alias name cannot be 'any'
╭─[typescript/tsc/testdata/tests/cases/conformance/types/typeAliases/reservedNamesInAliases.ts:2:6]
1 │ interface I {}