microsoft / microsoft/TypeScript
Importing a type with the same name as a namespace import, ideally alongside it
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.4k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 117
Description
Suggestion
🔍 Search Terms
import, type, module, namespace import,
✅ Viability Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
Being able to do write something like this:
import * as Foo, type { Foo } from './Foo.js';
A more conservative request would be:
import * as Foo from './Foo.js';
import type { Foo } from './Foo.js';
📃 Motivating Example
import * as Foo, type { Foo } from './Foo.js';
// no need to write Foo.Foo vvv
const someFunction = (someFoo: Foo) => ...
💻 Use Cases
It's a common pattern in functional programming to substitute classes with modules. this has several advantages (tree-shakability, no this binding, etc.).
These modules bundle functions which operate on some data and a type that identifies this data.
export type Foo = [string, (a: any) => unknown];
export const unit = (): Foo => ['', (x: any) => x];
export const concat = (self: Foo, a: Foo) =>
[self[0] + a[0], (x: any) => a[1](self[1](x))];
The problem is that when you need to mention the type of the data when it's imported as a namespace, you find yourself repeating the name twice:
import * as Foo from './Foo.js';
const someFunction = (someFoo: Foo.Foo) => ...
On top of looking a little bit awkward, this is really not convenient when the name gets longer, but you don't want to import members directly because a function name like concat is likely to create collisions and aliasing it to concatFoo is a chore and can make the imports section of a file very noisy and unreadable.
I encountered this problem in my one code but also in libraries like fp-ts and Effect.
A module can also export types that it requires, like input types, so the more ambitious request would not only be useful for mentioning the "constructor" type, although this use case is less important.
A workaround is to export an alias (something like Foo.Of or Foo.Self):
export { Foo as Self }
If we are talking about a library, this is something the maintainer needs to support and it's not that self-explanatory.
Another option is to create a type alias in the importing file
import * as Foo from './Foo.js';
type Foo = Foo.Foo;
If our type requires generics and type constraints, this becomes a real chore on top of requiring maintenance.
It would be consistent with the behaviour of the latter workaround to support the more conservative request. At current it yields the error Duplicate identifier 'Foo', which is a little weird since values and types live in different worlds.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No source file, test, or compiler entry point is named. Start by reviewing the proposed namespace and type-import examples and the existing duplicate-identifier behavior. Done means agreeing on and implementing the supported import form, with coverage for the motivating examples and no change to emitted JavaScript.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100