Description
Search Terms
Modules, export, import
Suggestion
It would be convienent if type identifiers could co-exist with import namespace identifiers.
Use Cases
A common pattern is to have a module where the module name is the same as the main type exported by that module.
Examples
Consider this module:
result.ts
export type Result<TError, TValue> =
| { readonly type: "Ok"; readonly value: TValue }
| { readonly type: "Err"; readonly error: TError };
export function Ok<TValue>(value: TValue): Result<never, TValue> {
return { type: "Ok", value };
}
export function Err<TError>(error: TError): Result<TError, never> {
return { type: "Err", error };
}
// .. more functions to work with the Result type
Now consider a consumer of this moudule:
import * as Result from "./result";
export function itsOk(): Result.Result<string, string> {
const ok: Result.Result<string, string> = Result.Ok("");
const err: Result.Result<string, string> = Result.Err("");
return ok;
}
There are execessive type annotations in this example but the point is that it is annoying ot have to refer to the type Result
as Result.Result
. I would like to refer to the type as only Result
but still have functions related to the module in a Result
namespace.
I was reading about the new import type
syntax in 3.8 and thought I might get away with something like this:
import * as Result from "./result";
import type { Result } from "./result";
export function itsOk(): Result<string, string> {
const ok: Result<string, string> = Result.Ok("")
const err: Result<string, string> = Result.Err("")
return ok;
}
This fails because there are duplicate identifiers. Typescript does allow duplicate identifiers that live either 100% in the type world or 100% in the concrete world. But imported namespace objects (import * as
) lives in both worlds so it does not currently work. Specifically namespace object can contain both types and concrete things.
However I think typescript should be able to infer from usage if I'm referring the type or the namespace when using it in a place where types can be used. If what is referenced could be found by looking at the usage then duplicate identifiers could be allowed. I believe some other languages does it this way.
In this example the type used does not have a dot so it is referring to the imported type rather than the imported namespace.
const ok: Result<string, string> = ..
In this example the type used has a dot so it is referring to the namespace object.
const ok: Result.OtherType = ..
I think this is not related to the ECMA standard for modules and import/export but rather something typescript decide how to handle because it is fully in the "type world" which gets erased when emitted to js.
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, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.