Repository: swc-project/swc (rust)

Issue:
Duplicate binding is incorrectly reported on the type
### Describe the bug

type is incorrectly identified as a duplicate binding in the lint phase.

### Input code

```typescript
import { Context, createContext } from 'react'
export const Context: Context<null> = createContext<null>(null)
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript"
    },
    "target": "es2022"
  },
  "module": {
    "type": "es6"
  },
  "isModule": true
}
```


### Playground link

[REPL](https://play.swc.rs/?version=1.2.148&code=H4sIAAAAAAAAA8vMLcgvKlGoVnDOzytJrSjRUUguSk0sSYVyFWoV0orycxXUgYLJJepcqRVg5cn5ecUlMC1WMIZNXmlOjp2CLaoREFENEKnJBQDnfKWWbwAAAA%3D%3D&config=H4sIAAAAAAAAA6tWyipOVrKqVipILCpOLQKxiivzShIrlKyUSioLUouTizILSpRqdZRKEovSU0uAwqnFRgZGRiCh3PyU0pxUkB6QUrCUGUg8s9gXKlNSVJpaCwCtpKCjZAAAAA%3D%3D)

### Expected behavior

swc should compile correctly

### Actual behavior

```
error: Duplicate binding
 --> input.ts:2:14
  |
2 | export const Context: Context<null> = createContext<null>(null)
  |              ^^^^^^^
  |
note: Context was declared at here
 --> input.ts:1:10
  |
1 | import { Context, createContext } from 'react'
  |          ^^^^^^^
```

### Version

1.2.148

### Additional context

Potential solutions
Move the lint phase to the back of ts strip.

Pull request:
fix(es/lint): ignore type binding in `Duplicate binding`
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
- fix #3848 

Patch:
diff --git a/crates/swc_ecma_lints/src/rules/duplicate_bindings.rs b/crates/swc_ecma_lints/src/rules/duplicate_bindings.rs
index a6e1b42b43..68ea7e561a 100644
--- a/crates/swc_ecma_lints/src/rules/duplicate_bindings.rs
+++ b/crates/swc_ecma_lints/src/rules/duplicate_bindings.rs
@@ -1,5 +1,6 @@
 use std::collections::hash_map::Entry;
 
+use ahash::AHashSet;
 use swc_common::{collections::AHashMap, errors::HANDLER, Span};
 use swc_ecma_ast::*;
 use swc_ecma_utils::ident::IdentLike;
@@ -14,6 +15,7 @@ pub fn duplicate_bindings() -> Box<dyn Rule> {
 #[derive(Debug, Default)]
 struct DuplicateBindings {
     bindings: AHashMap<Id, Span>,
+    type_bindings: AHashSet<Id>,
 
     var_decl_kind: Option<VarDeclKind>,
     is_pat_decl: bool,
@@ -50,6 +52,22 @@ impl DuplicateBindings {
 impl Visit for DuplicateBindings {
     noop_visit_type!();
 
+    fn visit_module(&mut self, m: &Module) {
+        m.visit_with(&mut TypeCollector {
+            type_bindings: &mut self.type_bindings,
+        });
+
+        m.visit_children_with(self);
+    }
+
+    fn visit_script(&mut self, s: &Script) {
+        s.visit_with(&mut TypeCollector {
+            type_bindings: &mut self.type_bindings,
+        });
+
+        s.visit_children_with(self);
+    }
+
     fn visit_assign_pat_prop(&mut self, p: &AssignPatProp) {
         p.visit_children_with(self);
 
@@ -88,19 +106,25 @@ impl Visit for DuplicateBindings {
     fn visit_import_default_specifier(&mut self, s: &ImportDefaultSpecifier) {
         s.visit_children_with(self);
 
-        self.add(&s.local, false);
+        if !self.type_bindings.contains(&s.local.to_id()) {
+            self.add(&s.local, false);
+        }
     }
 
     fn visit_import_named_specifier(&mut self, s: &ImportNamedSpecifier) {
         s.visit_children_with(self);
 
-        self.add(&s.local, false);
+        if !s.is_type_only && !self.type_bindings.contains(&s.local.to_id()) {
+            self.add(&s.local, false);
+        }
     }
 
     fn visit_import_star_as_specifier(&mut self, s: &ImportStarAsSpecifier) {
         s.visit_children_with(self);
 
-        self.add(&s.local, false);
+        if !self.type_bindings.contains(&s.local.to_id()) {
+            self.add(&s.local, false);
+        }
     }
 
     fn visit_pat(&mut self, p: &Pat) {
@@ -126,3 +150,17 @@ impl Visit for DuplicateBindings {
         self.var_decl_kind = old_var_decl_kind;
     }
 }
+
+struct TypeCollector<'a> {
+    type_bindings: &'a mut AHashSet<Id>,
+}
+
+impl Visit for TypeCollector<'_> {
+    fn visit_ts_entity_name(&mut self, n: &TsEntityName) {
+        n.visit_children_with(self);
+
+        if let TsEntityName::Ident(ident) = n {
+            self.type_bindings.insert(ident.to_id());
+        }
+    }
+}


Test patch:
diff --git a/crates/swc_ecma_lints/tests/fixture.rs b/crates/swc_ecma_lints/tests/fixture.rs
index 57ef25cda3..50f02fe799 100644
--- a/crates/swc_ecma_lints/tests/fixture.rs
+++ b/crates/swc_ecma_lints/tests/fixture.rs
@@ -7,7 +7,7 @@ use swc_ecma_lints::{
     rules::{all, LintParams},
 };
 use swc_ecma_parser::{lexer::Lexer, Parser, Syntax};
-use swc_ecma_transforms_base::resolver::resolver_with_mark;
+use swc_ecma_transforms_base::resolver::{resolver_with_mark, ts_resolver};
 use swc_ecma_utils::HANDLER;
 use swc_ecma_visit::VisitMutWith;
 
@@ -43,7 +43,11 @@ fn pass(input: PathBuf) {
         let mut m = parser.parse_module().unwrap();
         let top_level_mark = Mark::fresh(Mark::root());
 
-        m.visit_mut_with(&mut resolver_with_mark(top_level_mark));
+        if input.extension().unwrap() == "ts" || input.extension().unwrap() == "tsx" {
+            m.visit_mut_with(&mut ts_resolver(top_level_mark));
+        } else {
+            m.visit_mut_with(&mut resolver_with_mark(top_level_mark));
+        }
 
         let top_level_ctxt = SyntaxContext::empty().apply_mark(top_level_mark);
 
diff --git a/crates/swc_ecma_lints/tests/pass/issue-3848/1/input.ts b/crates/swc_ecma_lints/tests/pass/issue-3848/1/input.ts
new file mode 100644
index 0000000000..1a46d3b1a5
--- /dev/null
+++ b/crates/swc_ecma_lints/tests/pass/issue-3848/1/input.ts
@@ -0,0 +1,2 @@
+import { Context, createContext } from "react";
+export const Context: Context<null> = createContext<null>(null);