From my understanding, import type should only introduce a symbol in the type namespace, while import * as X should introduce a symbol in the value namespace, so I would expect both to coexist. But I’m hitting a Duplicate identifier error in TypeScript.

Minimal reproducible example:

// moduleX.ts export type X<T> = (value: T) => T export const map = <T>(f: (x: T) => T) => (x: X<T>): X<T> => v => f(x(v))

Usage:

import type { X } from "./moduleX" import * as X from "./moduleX"

Compiler error:

Duplicate identifier 'X'

Additional observations:

Each import compiles correctly when used alone

The error only occurs when both imports are present

Compilation is done using tsc (no Babel, SWC, or other transpilers)

Questions

Why does a namespace import (import * as X) conflict with a type-only import of the same name?

Does import * as X introduce a symbol into the type space in addition to the value space?

Is there any compiler option that allows these two imports to coexist without renaming, or is aliasing the type the intended and only solution?

Undershade's user avatar

New contributor

Undershade is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

2

Well, the

import * as X from "./moduleX"

namespace import (for X symbol here) creates both type and value namespace as single X symbol.

import type { X } from "./moduleX"

the above does not create a runtime binding but introduces a symbol that exists in the value namepace for type level queries. Since typeof operates on values, this means that symbol must exist in the value namespace, even if it is erased at emit time, so the duplication error is not typescript confusing namespaces, but rather intentionally rejecting multiple declarations of same type level symbol.

Sujal's user avatar

New contributor

Sujal is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

3 Comments

Welcome to SO! • This answer says something incorrect: import type can indeed affect the value namespace. For example, export class Foo {} and then import type { Foo } from "⋯" will cause a value named Foo to be in scope, you just can't use it in runtime code. You can still write type FooConstructor = typeof Foo though. • What do you mean that TypeScript is "confusing" between two namespaces? Are you saying this is a TS bug? Or that it intentionally wants to prevent a possible namespace collision? Or that there is a namespace collision?

2026-01-16T16:58:02.947Z+00:00

Yes, but the import type never creates a runtime binding but it provides a symbol that can be used in type-level typeof query. This does not mean that it occupies the value namespace, its just that type system retains the data about that value shape. The error is typescript seeing two declarations of X in the type namespace and i think its not a bug, but rather intentional so that it can prevent meaningless type reference.

2026-01-16T17:25:11.203Z+00:00

It certainly does occupy the value namespace. If you can write typeof XXX then XXX is in the value namespace. It has no runtime effect but it puts things in the value namespace. If you think this is not a bug then maybe edit so that you don't talk about TypeScript being "confused", which is ambiguous.

2026-01-16T17:55:41.313Z+00:00

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.