Enum type not defined at runtime

Viewed 53892

I have a problem where the Typescript compiler compiles my code successfully, yet the runtime gives me undefined type errors.

In my app I created a types.ts file with some things shared between multiple other ts files. It contains a string enum like:

enum MyEnum {
  One = "one";
  Two = "two";
}

When I define it like this. The compiler lets me use it in other ts files, and appears to be happy. However, at runtime I get the error "MyEnum is not defined".

I know of two ways to solve this:

  1. Define the enum in the file where it is used. But I don't think this will solve anything for other files that want to use it.
  2. Use "export" in the types.ts file, and import every type explicitly everywhere it is used.

I am quite new to Typescript, and I feel I might be misunderstanding something fundamental.

First, I don't get why the Typescript compiler happily compiles my code if there's going to be a runtime error. I would understand it if I had used the declare keyword, telling the compiler that something should be available at runtime, but in this case I don't see why it should assume that the enum comes from anywhere else then the types.ts file.

Second, I would like to define types somewhere globally in my app and have them be available everywhere without having to import them every time I used them. How do I accomplish this? Or is this maybe considered bad practice?

I am using Typescript 2.6 and my config looks like this:

{
  "compilerOptions": {
    /* Basic Options */
    "target": "es6",
    "module": "commonjs",
    "lib": ["es6", "es7", "esnext"],

    "sourceMap": true /* Generates corresponding '.map' file. */,
    "outDir": "build" /* Redirect output structure to the directory. */,
    "removeComments": true /* Do not emit comments to output. */,

    /* Strict Type-Checking Options */
    "strict": true /* Enable all strict type-checking options. */,

    /* Additional Checks */
    "noUnusedLocals": true /* Report errors on unused locals. */,
    "noUnusedParameters": true /* Report errors on unused parameters. */,
    "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
    "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,

    "plugins": [{ "name": "tslint-language-service" }],
    "skipLibCheck": true // because firebase-sdk has wrong type files now (Nov 18)
  },
  "include": ["src/**/*"],
  "exclude": ["build"]
}
8 Answers

There's another way you can do this. If you don't want to export your enum you can define it as a const enum

const enum MyEnum {
   One = "one";
   Two = "two";
}

These are inlined by the compiler and are completely removed during compilation.

In my case of undefined enum, it turned out it's because of circular import:

export enum A {...} defined in file a.ts, export const b = ... defined in file b.ts;

import {A} from './a.ts' in b.ts, while import {b} from './b.ts' in a.ts.

The error was gone after removing circular imports.

I had the same problem when imported re-exported enum. It caused runtime error.

layout.ts

export enum Part { Column, Row }

index.ts

export * from './layout'

component.ts

import { Part } from '../entities' // This causes error
import { Part } from '../entities/layout' // This works

I had this issue because I used declare in

export declare enum SomeEnum

Instead of

export enum SomeEnum

For us it turned out that simply restarting the application solved the problem. (Nativescript app)

I had this error and it went away as soon as I used the export keyword, i.e.

export enum MyEnum {
  One = "one";
  Two = "two";
}

And make sure you import it in the files where you are using it as well, i.e.

import { MyEnum } from '../types.ts';

I found that when I declared the enum without the export keyword, I could still reference the enum without importing it in other files without a compiler error - it was only at runtime the undefined exception was then thrown.

Here is my case, when I add a new enum to enum.ts file like this:

enum.ts:

enum RotateMode {
  UNKNOWN = 'UNKNOWN',
  OPTIMIZE = 'OPTIMIZE',
  ROTATE_FOREVER = 'ROTATE_FOREVER'
}

export { RotateMode };

I didn't run tsc to compile this file again. This cause the enum.js file doesn't have the RotateMode enum

enum.js:

// old file doesn't have the RotateMode enum

Then, I import the RotateMode enum to my index.ts file:

import { RotateMode } from './enum';

console.log(`RotateMode: ${JSON.stringify(RotateMode)}`);

The result is:

RotateMode: undefined

Somehow, the import syntax will import from .js file with high priority.

So, DON'T forget to compile the ts file after you add something new

I may be late to the party but beware of any @ts-ignore lines in the files blamed by the error stack.

My issue was that, after a pretty big refactoring, the import to the enum was cleaned by an IDE shortcut and the compiler couldn't detect it as missing because of the ts-ignore annotation which was targeted at something else (alas, on the same line).

Related