How to refer to Typescript enum in d.ts file, when using AMD?
The best solution may depend on whether you have a preference for the actual JavaScript variable being a number, a string, or otherwise. If you don't mind String, you can do it like this:
///messagelevel.d.ts
export type MessageLevel = "Unknown" | "Fatal" | "Critical" | "Error";
///main.d.ts
import * as ml from "./MessageLevel";
interface IMyMessage {
name: string;
level: ml.MessageLevel;
message: string;
}
So in the end JavaScript, it will simply be represented as a string, but TypeScript will flag an error anytime you compare it to a value not in that list, or try to assign it to a different string. Since this is the closest that JavaScript itself has to any kind of enum (eg, document.createElement("video")
rather than document.createElement(ElementTypes.VIDEO)
, it might be one of the better ways of expressing this logic.
I was thinking about this issue these last couple of days, and perhaps a const enum
, coupled with union types, may be a suitable option.
This approach depends on the fact that your API clients can expect some enum that is not explicitly declared in your API files.
Consider this. First, the API file api.d.ts
:
/**
* @file api.d.ts
*
* Here you define your public interface, to be
* implemented by one or more modules.
*/
/**
* An example enum.
*
* The enum here is `const` so that any reference to its
* elements are inlined, thereby guaranteeing that none of
* its members are computed, and that no corresponding
* JavaScript code is emmitted by the compiler for this
* type definition file.
*
* Note how this enum is named distinctly from its
* "conceptual" implementation, `MyEnum`.
* TypeScript only allows namespace merging for enums
* in the case where all namespaces are declared in the
* same file. Because of that, we cannot augment an enum's
* namespace across different source files (including
* `.d.ts` files).
*/
export const enum IMyEnum { A }
/**
* An example interface.
*/
export interface MyInterface {
/**
* An example method.
*
* The method itself receives `IMyEnum` only. Unfortunately,
* there's no way I'm aware of that would allow a forward
* declaration of `MyEnum`, like one would do in e.g. C++
* (e.g. declaration vs definition, ODR).
*/
myMethod(option: IMyEnum): void;
}
And an API implementation, impl.ts
:
/**
* @file impl.ts
*/
/**
* A runtime "conceptual" implementation for `IMyEnum`.
*/
enum MyEnum {
// We need to redeclare every member of `IMyEnum`
// in `MyEnum`, so that the values for each equally named
// element in both enums are the same.
// TypeScript will emit something that is accessible at
// runtime, for example:
//
// MyEnum[MyEnum["A"] = 100] = "A";
//
A = IMyEnum.A
}
class MyObject implements IMyInterface {
// Notice how this union-typed argument still matches its
// counterpart in `IMyInterface.myMethod`.
myMethod(option: MyEnum | IMyEnum): void {
console.log("You selected: " + MyEnum[option]);
}
}
// ----
var o = new MyObject();
o.myMethod(MyEnum.A); // ==> You selected: 100
o.myMethod(IMyEnum.A); // ==> You selected: 100
// YAY! (But all this work shouldn't really be necessary, if TypeScript
// was a bit more reasonable regarding enums and type declaration files...)
I made this gist as an example, in case someone would like to see this approach in action.