Reputation: 30009
I want to export a type with the same name as a value. Here's an example module:
// foo.ts
export type Foo = string;
export function create(): Foo {
// ...
}
What's the reason that this pattern works:
// index.ts
import * as _Foo from "./foo";
export const Foo = _Foo;
export type Foo = _Foo.Foo;
But these patterns don't?
// No compiler error but value is not exported
export type Foo = import("./foo").Foo;
export * as Foo from "./foo";
// Duplicate identifier error
import type { Foo } from "./foo";
import * as Foo from "./foo";
export { Foo };
// Individual declarations in merged declaration 'Foo' must be all exported or all local. [2395]
import * as Foo from "./foo";
export { Foo };
export type Foo = Foo.Foo;
Upvotes: 5
Views: 9345
Reputation: 3230
Type aliases do not produce a value.
Let me explain what is going on in your examples
// Types (Foo) and values (create) are imported into a single variable
import * as _Foo from "./foo";
// The module is reexported with another name
export const Foo = _Foo;
// The type is reexported with another name
export type Foo = _Foo.Foo;
Compiled version does not contain a type of course
var _Foo = require("./foo");
exports.Foo = _Foo;
After that, if Foo
is used as a type it is correctly resolved to string
, but if Foo
is used as a value it is an object with create
field.
The fun fact is that you can use typeof Foo
now and it will be { create: () => string }
Moving forward, the following is pretty the same as previous but because the first import explicitly states it is a type it is possible to use the same name for the value export
export type Foo = import("./foo").Foo; // does not produce a variable
export * as Foo from "./foo"; // named value export
Compiles into
exports.Foo = require("./foo");
The next one is a bit tricky, to be honest, I don't know why it does not work. I guess it may be a bug since import type
is a shiny new feature but I have not found it.
import type { Foo } from "./foo";
import * as Foo from "./foo";
export { Foo };
Locally defined type works:
type Foo = string;
import * as Foo from "./foo";
export { Foo };
And the last one, since Foo
is declared in this import cannot be merged with it. You can take a look at this issue for details.
import * as Foo from "./foo";
export { Foo };
export type Foo = Foo.Foo; // <- local definition
It can be rewritten using a temporary identifier, just you did it in the first example: (it does not work, see update below)
import * as _Foo from './Foo'
export { _Foo as Foo }
export type Foo = _Foo.Foo
Update
The last listed snippet produces correct js output but throws an error when the export is used as a value:
import * as _Foo from './merged'
const x: Foo = '1234'; // Ok
const y = Foo.create(); // <- TS2693: 'Foo' only refers to a type, but is being used as a value here.
It seems like a bug but I cannot find it posted
Upvotes: 3