How to correctly alias `enum` types (and their objects) in TypeScript?

Viewed 25

We're converting some code from the Closure type system to TypeScript. Previously we had some @enums and @typedefs that were exported as static members of a class:

export class C {}

/** @enum {number} */
C.E = {v0: 0, v2: 1, v2: 2};

/** @typedef { ... omitted ... } */
C.T;

It seems that TypeScript doesn't support declaring types to be static members of a class (either via the static keyword in the class declaration or via syntax like enum C.E {…; instead, it appears that the preferred way to maintain backwards compatibility is via declaration merging:

export class C {}

export namespace C {
  export enum E = {v0, v1, v2};
  export type T: /* omitted */;
}

and this works as expected.

We'd like to transition this module from exporting these types as static properties of the class to separate named exports in their own right—but export them in both places transitionally. How can one do this?

For the typedef, a simple export type T = C.T; seems to suffice, but for the enum it appears that one can write:

export type E = C.E;

to export the type but not the object (E will be undefined in the compiled JavaScript), or

export const E = C.E;

to export the object but not the type (E will be defined, but trying to use it as a type produces "'E' refers to a value, but is being used as a type here" errors).

Also, if we want to prepare for the removal of the static properties, is there some good way to move the declarations out of the merged namespace, while still reexporting them there? I.e., something like:

export class C {}
export enum E = {v0, v1, v2};
export type T: /* omitted */;

export namespace C {
  export type T = /* ??? */;  // T = T does not work, for obvious reasons.
  export /* ??? */ C = /* ??? */;
}
1 Answers

One solution which appears to work, but which seems sub-optimal, is to use declaration merging for the re-exports:

export type E = C.E;
export const E = C.E;
Related