DEV Community

Rungsikorn Rungsikavanich
Rungsikorn Rungsikavanich

Posted on

How to proper use ambient enum from Definition file

If you guy need to declare your enum in definition file you may encounter a weird behavior and lead to something like this

/// module A
/// types.d.ts
export enum MyEnumA {
     A = "A"
}


/// module B
/// main.ts
import { MyEnumA } from 'A'
console.log(MyEnumA.A) 
// TypeError: cannot read property 'A' of undefined
Enter fullscreen mode Exit fullscreen mode

Why?

In the nutshell, Definition file cannot be reference as value and that is why they call it 'Ambient'


How to properly solve it?

const enum solution (Not recommended)

If your project have a very simple setup. You may able to use the typescript feature call const enum which

const as a prefix of enum will turn this enum into an inline constant in compiler time.

/// module A
/// types.d.ts
export const enum MyEnumA {
     A = "A"
}


/// module B
/// main.ts
import { MyEnumA } from 'A'
console.log(MyEnumA.A) 

/// module B compiled
/// main.js
console.log("A") // <<< here is the hardcoded value from enum

Enter fullscreen mode Exit fullscreen mode

But why this approach is not recommended?

  1. this approach does not support isolatedModule option
  2. as MyEnumA.A is transpile into a hardcoded value. if module B enum value is change, you need to recompile module A again to update the hardcoded value
  3. Typescript team does not recommend it https://www.typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls

Declare ambient value in global scope (Recommended)

The proper solution is to assign this enum value ambient. As ambient is just an abstraction, in the implementation file you need to assign this value as you declare in ambient too.

So we will declare enum in definition file as an ambient and assign value into global scope variable

/// module A
/// types.d.ts
declare enum MyEnumAValue { // ambient enum
     A = "A"
}
declare var MyEnumA: typeof MyEnumAValue // ambient value

/// module B
/// types.ts as ambient value file
enum MyEnumAValue {
    A = 'A'
}

window.MyEnumA = MyEnumAValue; /// this will be checking with ambient if enum value is consistent with ambient enum
// or
global.MyEnumA = MyEnumAValue;


/// main.ts
import 'A'
console.log(MyEnumA.A) 
Enter fullscreen mode Exit fullscreen mode

Here is how the type checking work

Consistent

Image description

Inconsistent

Image description

Summary

If you want to use a type definition file and you want to declare enum in it.

in the implementation file you need to assign the value as you declare in the ambient too.

The best way to do it is assign the enum value to global scope variable.

Top comments (3)

Collapse
 
beraliv profile image
beraliv

Hey, thank you for your article!

Why do you need to declare enum in declaration file?

Collapse
 
zapkub profile image
Rungsikorn Rungsikavanich

@beraliv There is sometime that we may need to use our module as ambient module 🤓

Collapse
 
beraliv profile image
beraliv

For my benefit, could you please give me one example so I will understand you better?

I didn't use ambient enums before. Maybe you have an example in OSS library where I can have a look at the usage?