diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 0cd7616f02e51..44717e1f7dce1 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -577,6 +577,13 @@ pub fn accessibility_modifier_on_private_property(modifier: &Modifier) -> OxcDia .with_label(modifier.span) } +/// TS(2207) +#[cold] +pub fn type_modifier_on_named_type_export(span: Span) -> OxcDiagnostic { + ts_error("2207", "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.") + .with_label(span) +} + // ================================== TS ENUMS ================================= /// Computed property names are not allowed in enums.ts(1164) diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index b55fde263405d..1531195fbb99a 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -287,7 +287,7 @@ impl<'a> ParserImpl<'a> { Kind::RCurly, Kind::Comma, /* trailing_separator */ true, - Self::parse_export_named_specifier, + |parser| parser.parse_export_named_specifier(export_kind), ) })?; self.expect(Kind::RCurly)?; @@ -548,7 +548,10 @@ impl<'a> ParserImpl<'a> { ImportOrExportKind::Value } - fn parse_export_named_specifier(&mut self) -> Result> { + fn parse_export_named_specifier( + &mut self, + parent_export_kind: ImportOrExportKind, + ) -> Result> { let specifier_span = self.start_span(); let peek_kind = self.peek_kind(); // export { type} // name: `type` @@ -571,6 +574,11 @@ impl<'a> ParserImpl<'a> { } } + // `export type { type bar } from 'foo';` + if parent_export_kind == ImportOrExportKind::Type && export_kind == ImportOrExportKind::Type + { + self.error(diagnostics::type_modifier_on_named_type_export(self.cur_token().span())); + } if export_kind == ImportOrExportKind::Type { self.bump_any(); } diff --git a/tasks/coverage/snapshots/estree_typescript.snap b/tasks/coverage/snapshots/estree_typescript.snap index f7a1011f7b54f..f763b8f4182c3 100644 --- a/tasks/coverage/snapshots/estree_typescript.snap +++ b/tasks/coverage/snapshots/estree_typescript.snap @@ -2,7 +2,7 @@ commit: 15392346 estree_typescript Summary: AST Parsed : 10619/10725 (99.01%) -Positive Passed: 9097/10725 (84.82%) +Positive Passed: 9096/10725 (84.81%) Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts A class member cannot have the 'const' keyword. Mismatch: tasks/coverage/typescript/tests/cases/compiler/accessOverriddenBaseClassMember1.ts @@ -1668,6 +1668,8 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/externalModul Cannot use `await` as an identifier in an async context Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts `await` is only allowed within async functions and at the top levels of modules +Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/externalModules/typeOnly/exportSpecifiers.ts +The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement. tasks/coverage/typescript/tests/cases/conformance/externalModules/typeOnly/grammarErrors.ts Unexpected estree file content error: 3 != 4 diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index 5d1e903d3d8a3..bccfca6488db3 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -3,7 +3,7 @@ commit: 578ac4df parser_babel Summary: AST Parsed : 2303/2322 (99.18%) Positive Passed: 2282/2322 (98.28%) -Negative Passed: 1555/1673 (92.95%) +Negative Passed: 1556/1673 (93.01%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-startindex-and-startline-specified-without-startcolumn/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/startline-and-startcolumn-specified/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/startline-specified/input.js @@ -112,7 +112,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-arguments/empty-type-ref-babel-7/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-arguments/instantiation-expression-property-access/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-arguments/instantiation-expression-property-access-babel-7/input.ts -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/const-type-parameters-invalid/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/const-type-parameters-invalid-babel-7/input.ts @@ -12936,6 +12935,12 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc ╰──── help: Try insert a semicolon here + × TS(2207): The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/input.ts:1:15] + 1 │ export type { type foo } from "foo"; + · ──── + ╰──── + × Identifier expected. 'if' is a reserved word that cannot be used here. ╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-only-keyword/input.ts:1:7] 1 │ const if = {};