exporting enum from typescript type definition file
Asked Answered
S

5

45

I'm writing the type definitions for a library I'm using. One function in the library identifies the mouse button clicked by an integer:

 //index.d.ts
 export as namespace myLib;
 // activates the library listening for a specific mouse button
 function activate(button : number ) : void 

I introduced an enum to make this nicer:

//index.d.ts
export as namespace myLib;
export enum MouseButton {
    LEFT = 1,
    MIDDLE = 2,
    RIGHT = 4
}

export function activate(button : MouseButton ) : void;

Now, when I import this function and use it, everything compiles but I guess the enum is stripped and undefined when executed in the browser. The error message says Cannot read property 'LEFT' of undefined.

Therefore I rearranged the files like so:

//MouseButton.ts
export enum MouseButton {
    LEFT = 1,
    MIDDLE = 2,
    RIGHT = 4
}

//index.d.ts
export as namespace myLib;
import {MouseButton} from MouseButton;
export {MouseButton} from MouseButton;
export function activate(button : MouseButton ) : void;

Now I can import {MouseButton} from "myLib/MouseButton"; import * as myLib from "myLib". But this requires two imports. Referencing myLib.MouseButton still compiles but doesn't run.

Is there any way to import and reference the MouseButton enum via the myLib imported via the import * as myLib statement? I'm not only looking for an answer explaining how to do it but for one explaining why my solution doesn't work or why it isn't possible. Hints to resources explaining what's wrong are also appreciated

PS: I also tried the syntax suggested here re-export Typescript enum from namespace? but that didn't work either.

PPS: The module in question is a UMD module from the cornerstone project (https://github.com/cornerstonejs/cornerstone) used in an angular 6 project.

Slaton answered 28/5, 2018 at 10:54 Comment(7)
What is the source code where the error Cannot read property 'LEFT' of undefined occurred?Upheave
This code is part of an angular project (sorry for not mentioning that). The error message is thrown in the console of the executing browser.Slaton
Do you use the enum MouseButton as a reverse map : MouseButton[button] ? If not, I don't know why this pure TypeScript thing is leaking to JavaScript, unless a JaveScript bundle issue with the modules.Upheave
Perhaps, it's an issue related to the type definition syntax. It depends on the type of JavaScript library: what kind of module: UMD? pure ES6? ... Try to find a similar library to get inspired by its definition file. Without this library and its usage (i.e. more code), it's difficult to reason about this issue.Upheave
Nope. My usage of MouseButton is as follows: myLib.activate(MouseButton.LEFT); (with the two-imports version). but I would like it to be myLib.activate(myLib.MouseButton.LEFT)`. What do you mean by "leaking"? should it be compiled away and replaced by the enum's value?Slaton
The library is a UMD library. It's cornerstone (github.com/cornerstonejs/cornerstone) and the api I'm trying to call is cornerstoneTools.wwwc.activate .Slaton
Let us continue this discussion in chat.Slaton
U
36

(To complete t.animal's own answer)

Declaration files are difficult to make: see the long documentation. Sometimes looking in existing .d.ts files can help.

Regarding enum, declaring them as const enum is a clean and simple approach. It's what is done for jquery for instance, see @types/jquery/index.d.ts for Mouse and Key. It's handy because standard enums are compiled in JavaScript as arrays while const enum members are compiled directly as values ; see TypeScript Playground.

Upheave answered 28/5, 2018 at 14:45 Comment(2)
How to reexport it?Co
@Jose: I don't understand your question. Perhaps it deserves its own full stack overflow question.Upheave
S
34

Solved it by the help of Romain Denau's comment above. It nudged me in the right direction: What code does the typescript compiler generate from an enum (see https://www.typescriptlang.org/docs/handbook/enums.html#enums-at-runtime)? Declaring the enum const allows the typescript compiler to completely swap the identifier with the respective value, effectively inlining it. No more leakage of the enum into the production code. Thanks!

//index.d.ts
export as namespace myLib;

export const enum MouseButton {
    LEFT = 1,
    MIDDLE = 2,
    RIGHT = 4
}

export function activate(button : MouseButton ) : void;
Slaton answered 28/5, 2018 at 14:2 Comment(2)
"What code does the typescript compiler generate from an enum definition?" - this.Erechtheum
On the same page it also mentioned pitfalls of using const - typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls. Is that an issue for you? Also this article explained more on this topic: ncjamieson.com/dont-export-const-enums. I am confused.Hellenhellene
L
9

From my short research on the topic, I noticed that exporting the enums from the type definition file using export enum const is a bad idea. Since you have to enable the --isolatedModules flag which isn't even possible in say create-react-app and it can get messy.

Instead, I myself used the normal syntax in my shared.d.ts file:

  export enum EReviewStatus {
    PENDING = 'PENDING',
    SENT = 'SENT'
  }

And then I have a .js file, which is imported in the package.json main-block eg:

"main": "shared.js",

Where I have (using CommonJS export to make it compatible in both Node.js and frontend):

module.exports.EReviewStatus = {
  PENDING: 'PENDING',
  SENT: 'SENT'
}

Which works and I think is better practise, as now your code is clearly separated from the types.

Libriform answered 27/4, 2020 at 11:45 Comment(4)
I didn't get the shared.js file part. Could you be more precise on what you've done please ? I'm in a situation where I cannot disable --isolatedModules and your solution would save my lifeRedroot
@Redroot so I have two files: shared.d.ts and shared.js (the names are arbitrary). They are imported from the package.json using "types": "shared.d.ts" and "main": "shared.js". I don't know how else I can explain it. I updated my solution to use CommonJS since I wasnt able to import the js from my Node.js backend without it (and I dont want to compile it).Libriform
Yeah attempting to use path, composite, and references does not work as well as this option.Philips
This is not DRY. To make it DRY, you could have a shared.ts and generate the .js and .d.ts from that. Or just write the whole project as TS.Orthotropous
P
2

You can't export enum from .d.ts file. Just create another file maybe constants.ts and export the same enum from that file.
Because enum is treated more like a variable rather than a type or interface.
Hope it helps!.😊😊😊😊

Psych answered 16/1, 2023 at 3:4 Comment(1)
Found this the hard way. This should be the recommended answer.Lyall
U
0

I'm using a similar approach to @TeemuK:

// Define a constant you can export
export const ReviewStatusEnum {
    PENDING = 'PENDING',
    SENT = 'SENT'
} as const

// And infer the valid values from it for typing
export type ReviewStatusType = keyof typeof ReviewStatusEnum

This leaves you without the need of duplicating your enum values.

Ultra answered 21/3, 2024 at 15:47 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.