Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A heterogeneous enum in TypeScript is an enumeration that contains a mixture of both string and numeric member values. It combines the distinct compilation behaviors and type-checking rules of both numeric and string enums within a single data structure.
enum HeterogeneousEnum {
    No = 0,
    Yes = "YES",
    Pending = 1,
    Unknown // Implicitly resolves to 2
}

Initialization Rules

The TypeScript compiler enforces strict initialization rules when mixing types within an enum:
  • String Members: Must be explicitly initialized with a string literal.
  • Numeric Members: Can be explicitly initialized or implicitly auto-incremented.
  • Positional Dependency: If an uninitialized member immediately follows a string member, the compiler will throw an error (TS1061: Enum member must have initializer). Auto-incrementing is only valid if the preceding member resolves to a numeric value.
enum InvalidEnum {
    A = "TEXT",
    B // Error: Enum member must have initializer.
}

Reverse Mapping Behavior

TypeScript handles the transpilation of string and numeric enum members differently. Numeric enums generate a reverse mapping (value-to-key) in the emitted JavaScript, whereas string enums only generate a forward mapping (key-to-value). In a heterogeneous enum, the compiler applies these rules on a per-member basis. Only the numeric members will populate the reverse mapping object. TypeScript Source:
enum Status {
    Active = "ACTIVE",
    Code = 100
}
Emitted JavaScript:
var Status;
(function (Status) {
    // Forward mapping only for string member
    Status["Active"] = "ACTIVE";
    
    // Forward and reverse mapping for numeric member
    Status[Status["Code"] = 100] = "Code";
})(Status || (Status = {}));
If you inspect the resulting Status object at runtime, it evaluates to:
{
    "Active": "ACTIVE",
    "Code": 100,
    "100": "Code" // Reverse mapping exists only for the numeric member
}

Type Resolution

At the type level, a heterogeneous enum creates a union type of its unique, opaque members (Config.Path | Config.Timeout). TypeScript treats string enum members as nominal types. This means the raw string literal "/usr/bin" is not assignable to Config.Path, and the enum’s underlying type is not structurally equivalent to a standard literal union like "/usr/bin" | 5000. When assigning enum members to variables, TypeScript’s type inference behaves differently depending on the declaration keyword:
enum Config {
    Path = "/usr/bin",
    Timeout = 5000
}

// Explicitly typed as the enum union: Config.Path | Config.Timeout
let explicitValue: Config = Config.Path; 

// Widens to the parent enum type: Config
let widenedValue = Config.Path;

// Inferred strictly as the specific literal member type: Config.Path
const inferredValue = Config.Path;
Because of the structural differences in reverse mapping and the lack of implicit type coercion between strings and numbers, bitwise operations (which are common with numeric enums) cannot be safely applied to the string members of heterogeneous enums. Attempting to apply bitwise operators to a string member results in a strict compiler error. Because TypeScript treats these members as nominal types, the error references the specific enum member’s literal type rather than the primitive string type (e.g., Operator '&' cannot be applied to types 'Config.Path' and 'number').
Master TypeScript with Deep Grasping Methodology!Learn More