Repository: TypeScript
Issue: Suggestion: Allow merging of a wildcard import alias with a compile-time-only type alias of the same name
Labels: Suggestion,Awaiting More Feedback

For projects with a lot of types with their own sets of functions, namespacing is of course a standard technique in programming to resolve naming conflicts between the functions of those types. The problem with namespacing in TypeScript (and JavaScript, by extension) is that it introduces an object-property hierarchy that can't be easily normalized to eliminate dead code and unused functions and classes.

ES2015 module syntax provided a nice solution for this - wildcard imports - but unfortunately that doesn't play nicely with TypeScript's merging of namespaces with types. I'd love to see a way to associate a module with an interface or type alias without introducing the compiled output overhead of TypeScript namespaces when those namespaces exist only to associate some functions with a related type name.

### Proposal

If a compile-time-only type is imported with the same alias as the wildcard alias, either as `default` or otherwise, that type name would automatically merge with the wildcard alias, allowing the wildcard alias to also be used to reference the merged type alias:

```ts
// foo.ts
export interface Foo { /* ... */ }
export function create(): Foo { /* ... */ }
export default Foo;

// bar.ts
import Foo, * as Foo from './foo';
const foo: Foo = Foo.create();

// ------
// or:

// bar.ts
import * as Foo from './foo';
import { Foo } from './foo';
const foo: Foo = Foo.create();
```

### Rationale

This is sort of already supported with TypeScript namespace merging, but TypeScript namespaces generate extra boilerplate that can't be easily tree-shaken at build time, and for which the indirection is baked into the final output, unlike wildcard imports, the indirection of which is discarded at build time by modern bundlers.

I can't do this:

```ts
import * as SortedSet from '@collectable/sorted-set';

// redundant/contrived, just for explanation purposes:
function add<T>(value: T, set: SortedSet<T>): SortedSet<T> {
  return SortedSet.add(value, set);
}
```

or this:

```ts
// ... the names can't be merged here:

import SortedSet, * as SortedSet from '@collectable/sorted-set';

// or here:

import * as SortedSet from '@collectable/sorted-set';
import SortedSet from '@collectable/sorted-set';

// or here:

import * as SortedSet from '@collectable/sorted-set';
import { SortedSet } from '@collectable/sorted-set';
```

Instead, I'd have to do this:

```ts
import * as SortedSet from '@collectable/sorted-set';

function add<T>(value: T, set: SortedSet.SortedSet<T>): SortedSet.SortedSet<T> {
  return SortedSet.add(value, set);
}
```

Or I could give SortedSet some kind of general type name, but this would then be too abstract if not performing a wildcard import:

```ts
import * as SortedSet from '@collectable/sorted-set';

function add<T>(value: T, set: SortedSet.Type<T>): SortedSet.Type<T> {
  return SortedSet.add(value, set);
}

// but now I'm forced to manually alias the type at the point of consumption:

import {Type as SortedSet, add} from '@collectable/sorted-set';

function add<T>(value: T, set: SortedSet<T>): SortedSet<T> {
  return add(value, set);
}
```

Alternatively I could export an alias of the `SortedSet` type as `Type`, but now I'm confusing the issue by having two different type names that mean the same thing.

I'm open to any solution which lets me associate a module with an interface or type alias; I just want the output to be optimal for ES2015, rather than optimal for TypeScript. For libraries that export a number of different type modules, where those modules implement common function names, this would be a very useful addition.