fix(parser): require arrow separator in TypeScript function types (#26529)

Require `=>` when parsing TypeScript function and constructor types, while retaining `:` for function, method, and arrow return annotations. Previously, any token could be consumed as the separator, accepting invalid types such as `type F = (): number` and `type F = new () + number`.

This also prevents a valid conditional expression from being silently grouped into the wrong arrows:

```ts
const a = true, b = 1, c = 2;
const f = a ? (b) : (): any => b ? c : (x = 1): any => x;
console.log(typeof f);
```

The transformed program now prints `number`; previously it printed `function`.
This commit is contained in:
camc314
2026-09-10 13:05:18 +00:00
parent 42315366b1
commit d6b6705f8f
21 changed files with 103 additions and 30 deletions
+4 -4
View File
@@ -53,7 +53,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
});
let return_type = {
let return_type_start = self.cur_start();
let return_type = self.parse_return_type();
let return_type = self.parse_return_type(Kind::Arrow);
TSTypeAnnotation::boxed(self.end_span(return_type_start), return_type, self)
};
@@ -1322,12 +1322,12 @@ impl<'a, C: Config> ParserImpl<'a, C> {
return None;
}
let start = self.cur_start();
let return_type = self.parse_return_type();
let return_type = self.parse_return_type(Kind::Colon);
Some(TSTypeAnnotation::boxed(self.end_span(start), return_type, self))
}
fn parse_return_type(&mut self) -> TSType<'a> {
self.bump_any();
fn parse_return_type(&mut self, separator: Kind) -> TSType<'a> {
self.expect(separator);
self.context_remove(Context::DisallowConditionalTypes, Self::parse_type_or_type_predicate)
}
@@ -0,0 +1 @@
type F = abstract new () : number;
@@ -0,0 +1 @@
type F = abstract new () number;
@@ -0,0 +1 @@
type F = abstract new () + number;
@@ -0,0 +1 @@
type F = new () : number;
@@ -0,0 +1 @@
type F = new () number;
@@ -0,0 +1 @@
type F = new () + number;
@@ -0,0 +1 @@
type F = () : number;
@@ -0,0 +1 @@
type F = () + number;
@@ -0,0 +1,7 @@
type F = () => number;
type C = new () => object;
type A = abstract new () => object;
type Predicate = (x: unknown) => x is number;
function f(): number { return 1; }
const arrow = (): number => 1;
const object = { method(): number { return 1; } };
+2 -2
View File
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 103/103 (100.00%)
Positive Passed: 103/103 (100.00%)
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
formatter_misc Summary:
AST Parsed : 103/103 (100.00%)
Positive Passed: 103/103 (100.00%)
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
lexer_misc Summary:
AST Parsed : 305/305 (100.00%)
Positive Passed: 305/305 (100.00%)
AST Parsed : 314/314 (100.00%)
Positive Passed: 314/314 (100.00%)
+59 -3
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 103/103 (100.00%)
Positive Passed: 103/103 (100.00%)
Negative Passed: 202/202 (100.00%)
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
Negative Passed: 210/210 (100.00%)
× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
@@ -4762,6 +4762,62 @@ Negative Passed: 202/202 (100.00%)
· ╰── `,` expected
╰────
× Expected `=>` but found `:`
╭─[misc/fail/ts-abstract-constructor-type-colon-separator.ts:1:26]
1 │ type F = abstract new () : number;
· ┬
· ╰── `=>` expected
╰────
× Expected `=>` but found `number`
╭─[misc/fail/ts-abstract-constructor-type-missing-separator.ts:1:27]
1 │ type F = abstract new () number;
· ───┬──
· ╰── `=>` expected
╰────
× Expected `=>` but found `+`
╭─[misc/fail/ts-abstract-constructor-type-plus-separator.ts:1:26]
1 │ type F = abstract new () + number;
· ┬
· ╰── `=>` expected
╰────
× Expected `=>` but found `:`
╭─[misc/fail/ts-constructor-type-colon-separator.ts:1:17]
1 │ type F = new () : number;
· ┬
· ╰── `=>` expected
╰────
× Expected `=>` but found `number`
╭─[misc/fail/ts-constructor-type-missing-separator.ts:1:18]
1 │ type F = new () number;
· ───┬──
· ╰── `=>` expected
╰────
× Expected `=>` but found `+`
╭─[misc/fail/ts-constructor-type-plus-separator.ts:1:17]
1 │ type F = new () + number;
· ┬
· ╰── `=>` expected
╰────
× Expected `=>` but found `:`
╭─[misc/fail/ts-function-type-colon-separator.ts:1:13]
1 │ type F = () : number;
· ┬
· ╰── `=>` expected
╰────
× Expected `=>` but found `+`
╭─[misc/fail/ts-function-type-plus-separator.ts:1:13]
1 │ type F = () + number;
· ┬
· ╰── `=>` expected
╰────
× TS(1009): Trailing comma not allowed.
╭─[misc/fail/ts-interface-invalid-heritage.ts:1:22]
1 │ interface A extends B, {}
@@ -7560,21 +7560,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
15 │ }
╰────
× Expected `,` or `)` but found `var`
╭─[typescript/tests/cases/compiler/functionTypesLackingReturnTypes.ts:2:11]
× Expected `=>` but found `)`
╭─[typescript/tests/cases/compiler/functionTypesLackingReturnTypes.ts:2:17]
1 │ // Error (no '=>')
2 │ function f(x: ()) {
· ┬
· ╰── Opened here
· ┬
· ╰── `=>` expected
3 │ }
╰────
╭─[typescript/tests/cases/compiler/functionTypesLackingReturnTypes.ts:6:1]
5 │ // Error (no '=>')
6 │ var g: (param);
· ─┬─
· ╰── `,` or `)` expected
7 │
╰────
× Identifier `total` has already been declared
╭─[typescript/tests/cases/compiler/functionWithSameNameAsField.ts:2:12]
+2 -2
View File
@@ -1,6 +1,6 @@
semantic_misc Summary:
AST Parsed : 103/103 (100.00%)
Positive Passed: 99/103 (96.12%)
AST Parsed : 104/104 (100.00%)
Positive Passed: 100/104 (96.15%)
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
Bindings mismatch:
after transform: ScopeId(0): ["private"]
@@ -1,3 +1,3 @@
transformer_misc Summary:
AST Parsed : 103/103 (100.00%)
Positive Passed: 103/103 (100.00%)
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
@@ -1,6 +1,6 @@
commit: 1eac4481
Passed: 276/404
Passed: 277/405
# All Passed:
* babel-plugin-transform-class-static-block
@@ -48,7 +48,7 @@ x Output mismatch
x Output mismatch
# babel-plugin-transform-typescript (41/60)
# babel-plugin-transform-typescript (42/61)
* allow-declare-fields-false/input.ts
Unresolved references mismatch:
after transform: ["dce"]
@@ -0,0 +1,3 @@
const a = true, b = 1, c = 2;
const f = a ? (b) : (): any => b ? c : (x = 1): any => x;
console.log(typeof f);
@@ -0,0 +1,3 @@
{
"plugins": ["transform-typescript"]
}
@@ -0,0 +1,3 @@
const a = true, b = 1, c = 2;
const f = a ? b : () => b ? c : (x = 1) => x;
console.log(typeof f);