TLDR: Do not define your enums in your TypeScript Declaration (*.d.ts) files, ever.

As a developer coming from a C# world I was dealing today with a rather strange error when implementing new functionality for one of our products.

I had to implement couple of services, and while doing so, I managed to define one of my enums in a Declaration file (previously called as Definition files or Typings files). I didn’t care about where it sits at that time as I would move all the pieces to correct locations afterwards. The file content were looking similar to the ones below:

// file: foo.service.d.ts

export enum FooEnum {
    EnumValue = 1
}

export interface IFooService {
    doFoo(foo: FooEnum):void;
}
// file: foo.service.ts

import {FooEnum, IFooService} from './foo.service.d'

export class FooService implements IFooService {
    doFoo(foo:FooEnum):void {
    }
}

Suprisingly enough, the app didn’t work and I was getting an Cannot read property 'EnumValue' of undefined error (or Error: Cannot find module './foo.service.d' when running app in NodeJs).

// file: app.ts

import {FooEnum} from './foo.service.d'
import {FooService} from './foo.service'

let service = new FooService();
service.doFoo(FooEnum.EnumValue);

The error dissapeared when I moved the enum definition into proper TypeScript file. The tricky part was that TypeScript compiled app just fine and error occured only after running the app.

The example application can be found on my GitHub.

Update

There has been a discussion on stackoverflow recently about the same matter for AMD specification.

The best suggestion is to export enum as a variable being a number/string from the TypeScript Declaration file. This ensures TypeScript validation when assigning the enum property value outside of the enum. The code below is taken from the stackoverflow thread.

///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;
}

Other potential solution could be to implement only enum interface in Typings Declaration file and then implement the actual enum in Typescript file (also discribed on stackoverflow thread).

What are TypeScript Declaration files

Just a quick summary what TypeScript Declaration files are:

  • Provides type definitions for any JavaScript (JS) file - enhancing JS development with AutoCompletion, type checking etc.
  • Are not transpiled into JS files when running TypeScript compiler as therefore cannot contain any functionality
  • Can be written additionally to any of the JS code
  • Exists for most of the JS frameworks available
  • Are currently available through npm under @Types organisation and can be installed by running npm install @Types/<package-name> --save-dev

Resources