diff --git a/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs b/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs index 607643b279a45..0c29635f85a5b 100644 --- a/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs +++ b/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs @@ -1517,6 +1517,14 @@ fn test() { // ", // None, // ), + ( + "import { Bar } from './bar'; +export type { Baz } from './baz'; + +export class Foo extends Bar {} +", + None, + ), ]; let fail = vec![ diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 9503630f0f2ff..7a98ecb01f443 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -1861,25 +1861,27 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { self.visit_declaration(declaration); } - for specifier in &it.specifiers { - // `export type { a }` or `export { type a }` -> `a` is a type reference - if it.export_kind.is_type() || specifier.export_kind.is_type() { - self.current_reference_flags = ReferenceFlags::Type; - } else { - // If the export specifier is not a explicit type export, we consider it as a potential - // type and value reference. If it references to a value in the end, we would delete the - // `ReferenceFlags::Type` flag in `fn resolve_references_for_current_scope`. - self.current_reference_flags = ReferenceFlags::Read | ReferenceFlags::Type; - } - self.visit_export_specifier(specifier); - } - if let Some(source) = &it.source { self.visit_string_literal(source); + self.visit_export_specifiers(&it.specifiers); + } else { + for specifier in &it.specifiers { + // `export type { a }` or `export { type a }` -> `a` is a type reference + if it.export_kind.is_type() || specifier.export_kind.is_type() { + self.current_reference_flags = ReferenceFlags::Type; + } else { + // If the export specifier is not a explicit type export, we consider it as a potential + // type and value reference. If it references to a value in the end, we would delete the + // `ReferenceFlags::Type` flag in `fn resolve_references_for_current_scope`. + self.current_reference_flags = ReferenceFlags::Read | ReferenceFlags::Type; + } + self.visit_export_specifier(specifier); + } } if let Some(with_clause) = &it.with_clause { self.visit_with_clause(with_clause); } + self.leave_node(kind); } diff --git a/crates/oxc_semantic/tests/fixtures/oxc/ts/issue-7879.snap b/crates/oxc_semantic/tests/fixtures/oxc/ts/issue-7879.snap new file mode 100644 index 0000000000000..d45c6096c3b79 --- /dev/null +++ b/crates/oxc_semantic/tests/fixtures/oxc/ts/issue-7879.snap @@ -0,0 +1,43 @@ +--- +source: crates/oxc_semantic/tests/main.rs +input_file: crates/oxc_semantic/tests/fixtures/oxc/ts/issue-7879.ts +--- +[ + { + "children": [ + { + "children": [], + "flags": "ScopeFlags(StrictMode)", + "id": 1, + "node": "Class(Foo)", + "symbols": [] + } + ], + "flags": "ScopeFlags(StrictMode | Top)", + "id": 0, + "node": "Program", + "symbols": [ + { + "flags": "SymbolFlags(Import)", + "id": 0, + "name": "Bar", + "node": "ImportSpecifier(Bar)", + "references": [ + { + "flags": "ReferenceFlags(Read)", + "id": 0, + "name": "Bar", + "node_id": 17 + } + ] + }, + { + "flags": "SymbolFlags(Class)", + "id": 1, + "name": "Foo", + "node": "Class(Foo)", + "references": [] + } + ] + } +] diff --git a/crates/oxc_semantic/tests/fixtures/oxc/ts/issue-7879.ts b/crates/oxc_semantic/tests/fixtures/oxc/ts/issue-7879.ts new file mode 100644 index 0000000000000..8a0d8a108d6da --- /dev/null +++ b/crates/oxc_semantic/tests/fixtures/oxc/ts/issue-7879.ts @@ -0,0 +1,4 @@ +import { Bar } from "./bar"; +export type { Baz } from "./baz"; + +export class Foo extends Bar {}