Typescript: Enums from d.ts with auto-calculated value leads to run-time error

Created on 15 Jan 2015  ·  5Comments  ·  Source: microsoft/TypeScript

tsc compiles code in module B to (param === A.T.B) w/o inlining the value of enum.
The problem is it leads to ReferenceError in run-time. But if I change declaration to
export enum T { A = 1, B = 2, C } it inlines value properly.

a.d.ts:
declare module A {
  export enum T { A = 1, B, C }
}

b.ts:
/// <reference path="a.d.ts" />
module B {
  class B1 {
    public fn2 (): void {}
    private fn (param: A.T): void {
      if (param === A.T.B) { // enum const is not in-lined -> run-time error
        fn2();
      }
    }
  }
}
By Design

Most helpful comment

declare enum is to describe enums that actually do exist. If you don't have an object called A.T at runtime, this code isn't guaranteed to work. The compiler does not inline A.T.B because B is assumed to have a calculated (non-constant value) because it was not given an explicit initializer.

A good alternative is to use const enum instead as these are guaranteed to be inlined.

All 5 comments

declare enum is to describe enums that actually do exist. If you don't have an object called A.T at runtime, this code isn't guaranteed to work. The compiler does not inline A.T.B because B is assumed to have a calculated (non-constant value) because it was not given an explicit initializer.

A good alternative is to use const enum instead as these are guaranteed to be inlined.

If I remove "declare", situation would be the same.
http://www.typescriptlang.org/Handbook#basic-types-enum
This tutorial implies that even if _value_ is not provided for some enum member, it will be automatically derived. Now it turns out that it also affects inlining of non-constant enums.
Sorry, but this is called "leaky abstraction".

The handbook is meant to be a resource for getting started with the language, it is not meant to comprehensively cover every nuance. The language specification defines this behavior more specifically:

12.1.4 Ambient Enum Declarations
_An AmbientEnumMember that includes a ConstantEnumValue value is considered a constant member. An AmbientEnumMember with no ConstantEnumValue value is considered a computed member._

and

9.2 Enum Members
_Enum members are either constant members or computed members. Constant members have known constant values that are substituted in place of references to the members in the generated JavaScript code. Computed members have values that are computed at run-time and not known at compile-time. No substitution is performed for references to computed members._

I don't see what you think the behavior _should_ be. Consider if you did something like this:

enum X {
  A = 1,
  B = 2,
  C = someRandomExpression() // Legal
}

The _correct_ ambient declaration for X looks like this:

declare enum X {
  A = 1,
  B = 2,
  C // Note: Not 3!
}

If we inlined in 3 in place of X.C, that would be wrong. A missing initializer in an ambient enum means that the enum value cannot be assumed to be any constant value.

Note that if you run tsc --d on a file that contains automatically-numbered enums (e.g. enum A { x, y, z }), all the initializers are put in place for you (enum A { x = 0, y = 1, z = 2 }). The compiler is entirely consistent in this regard.

This worked for me.

enums.d.ts

declare const enum ErrorType {
    CustomResponse = 1,
    SAPResponse = 2,
    WorkOrderResponse = 3,
    LocalBackendResponse = 4
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

kyasbal-1994 picture kyasbal-1994  ·  3Comments

siddjain picture siddjain  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments

fwanicka picture fwanicka  ·  3Comments

weswigham picture weswigham  ·  3Comments