diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index 229c49a78d0cd..48121dc68d0b7 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -199,15 +199,16 @@ pub struct ParseOptions { /// [`return`]: oxc_ast::ast::ReturnStatement pub allow_return_outside_function: bool, - /// Emit [`ParenthesizedExpression`]s in AST. + /// Emit [`ParenthesizedExpression`]s and [`TSParenthesizedType`] in AST. /// /// If this option is `true`, parenthesized expressions are represented by - /// (non-standard) [`ParenthesizedExpression`] nodes that have a single `expression` property - /// containing the expression inside parentheses. + /// (non-standard) [`ParenthesizedExpression`] and [`TSParenthesizedType`] nodes + /// that have a single `expression` property containing the expression inside parentheses. /// /// Default: `true` /// /// [`ParenthesizedExpression`]: oxc_ast::ast::ParenthesizedExpression + /// [`TSParenthesizedType`]: oxc_ast::ast::TSParenthesizedType pub preserve_parens: bool, /// Allow V8 runtime calls in the AST. diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index 6dd5e52fe70ba..71b9585cf4b75 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -936,7 +936,11 @@ impl<'a> ParserImpl<'a> { self.bump_any(); // bump `(` let ty = self.parse_ts_type()?; self.expect(Kind::RParen)?; - Ok(self.ast.ts_type_parenthesized_type(self.end_span(span), ty)) + Ok(if self.options.preserve_parens { + self.ast.ts_type_parenthesized_type(self.end_span(span), ty) + } else { + ty + }) } fn parse_literal_type_node(&mut self, negative: bool) -> Result> { diff --git a/napi/parser/index.d.ts b/napi/parser/index.d.ts index 6616982e57825..f59ab092d4d89 100644 --- a/napi/parser/index.d.ts +++ b/napi/parser/index.d.ts @@ -151,11 +151,11 @@ export interface ParserOptions { */ astType?: 'js' | 'ts' /** - * Emit `ParenthesizedExpression` in AST. + * Emit `ParenthesizedExpression` and `TSParenthesizedType` in AST. * * If this option is true, parenthesized expressions are represented by - * (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property - * containing the expression inside parentheses. + * (non-standard) `ParenthesizedExpression` and `TSParenthesizedType` nodes that + * have a single `expression` property containing the expression inside parentheses. * * @default true */ diff --git a/napi/parser/src/types.rs b/napi/parser/src/types.rs index 1de8c3b585ea8..f4d8a83be898b 100644 --- a/napi/parser/src/types.rs +++ b/napi/parser/src/types.rs @@ -22,11 +22,11 @@ pub struct ParserOptions { #[napi(ts_type = "'js' | 'ts'")] pub ast_type: Option, - /// Emit `ParenthesizedExpression` in AST. + /// Emit `ParenthesizedExpression` and `TSParenthesizedType` in AST. /// /// If this option is true, parenthesized expressions are represented by - /// (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property - /// containing the expression inside parentheses. + /// (non-standard) `ParenthesizedExpression` and `TSParenthesizedType` nodes that + /// have a single `expression` property containing the expression inside parentheses. /// /// @default true pub preserve_parens: Option, diff --git a/napi/parser/test/parse.test.ts b/napi/parser/test/parse.test.ts index ef95202d71326..b640dd095d46b 100644 --- a/napi/parser/test/parse.test.ts +++ b/napi/parser/test/parse.test.ts @@ -2,6 +2,7 @@ import { Worker } from 'node:worker_threads'; import { describe, expect, it } from 'vitest'; import { parseAsync, parseSync } from '../index.js'; +import type { ExpressionStatement, TSTypeAliasDeclaration } from '../index.js'; describe('parse', () => { const code = '/* comment */ foo'; @@ -244,6 +245,25 @@ describe('parse', () => { }); }); }); + + describe('preserveParens', () => { + it('should include parens when true', () => { + let ret = parseSync('test.js', '(x)'); + expect((ret.program.body[0] as ExpressionStatement).expression.type).toBe('ParenthesizedExpression'); + + ret = parseSync('test.ts', 'type Foo = (x)'); + expect((ret.program.body[0] as TSTypeAliasDeclaration).typeAnnotation.type).toBe('TSParenthesizedType'); + }); + + it('should omit parens when false', () => { + const options = { preserveParens: false }; + let ret = parseSync('test.js', '(x)', options); + expect((ret.program.body[0] as ExpressionStatement).expression.type).toBe('Identifier'); + + ret = parseSync('test.ts', 'type Foo = (x)', options); + expect((ret.program.body[0] as TSTypeAliasDeclaration).typeAnnotation.type).toBe('TSTypeReference'); + }); + }); }); describe('UTF-16 span', () => {