This TypeScript error occurs when an enum member lacks an explicit initializer in contexts where TypeScript cannot automatically infer its value. This happens after computed members, in string enums, or when mixing numeric and string values in heterogeneous enums.
TypeScript enums allow you to define a set of named constants. By default, numeric enums auto-increment starting from 0, so you don't need explicit initializers for every member. However, TypeScript enforces error TS1061 when it cannot determine what value an enum member should have. This restriction applies in three key scenarios: 1. **After computed members**: When an enum member's value is computed (e.g., from a function call), TypeScript cannot infer what the next member's value should be 2. **In string enums**: String enum members cannot auto-increment, so every member must be explicitly initialized 3. **In heterogeneous enums**: When mixing strings and numbers, uninitialized members must follow numeric constants (not strings or computed values) The error exists to prevent ambiguity and ensure enum values are predictable and deterministic. Without this rule, the runtime value of enum members could be unclear or inconsistent.
If you have a computed member (a function call or calculation), all subsequent members must have explicit initializers:
// BEFORE: Error TS1061
enum Status {
Active = 1,
Pending = getStatusCode(), // Computed member
Inactive, // Error: Enum member must have initializer
}
// AFTER: Add explicit initializer
enum Status {
Active = 1,
Pending = getStatusCode(), // Computed member
Inactive = 3, // Explicit value
}This happens because TypeScript cannot know the runtime value of getStatusCode(), so it cannot auto-increment from there.
String enums require every member to have an explicit string initializer:
// BEFORE: Error TS1061
enum Color {
Red = "RED",
Green, // Error: Enum member must have initializer
Blue = "BLUE",
}
// AFTER: Initialize all string members
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}Unlike numeric enums, string enums cannot auto-increment, so you must provide a value for each member.
In heterogeneous (mixed) enums, place all uninitialized members first or after numeric constants:
// BEFORE: Error TS1061 - uninitialized member after string
enum Mixed {
No = 0,
Yes = "YES",
Unknown, // Error: cannot auto-increment after string
}
// OPTION 1: Place uninitialized members first
enum Mixed {
Unknown, // Auto-assigned 0
Yes = "YES",
No = 2,
}
// OPTION 2: Initialize all members explicitly
enum Mixed {
No = 0,
Yes = "YES",
Unknown = 2,
}Uninitialized members can only follow numeric constant members, not string or computed values.
If you want automatic numbering, stick to numeric enums and avoid mixing in strings or computed values:
// Simple numeric enum - auto-increments from 0
enum Priority {
Low, // 0
Medium, // 1
High, // 2
Critical, // 3
}
// Or start from a specific number
enum HttpStatus {
OK = 200,
Created, // 201
Accepted, // 202
NoContent = 204,
BadRequest = 400,
Unauthorized, // 401
Forbidden, // 402
}This pattern avoids the initializer error entirely by relying on TypeScript's auto-increment feature.
If possible, replace runtime-computed values with compile-time constants:
// BEFORE: Computed member breaks auto-increment
enum ErrorCode {
Success = 0,
NotFound = calculateErrorCode("NOT_FOUND"), // Computed
ServerError, // Error: needs initializer
}
// AFTER: Use constant values
const NOT_FOUND_CODE = 404;
const SERVER_ERROR_CODE = 500;
enum ErrorCode {
Success = 0,
NotFound = NOT_FOUND_CODE,
ServerError = SERVER_ERROR_CODE,
}This allows TypeScript to know the exact values at compile time, avoiding the need for explicit initializers on subsequent members.
### When to Use Heterogeneous Enums
The TypeScript documentation explicitly states that mixing string and numeric values in enums is generally discouraged. Heterogeneous enums are confusing and rarely needed. Consider using separate enums or a union type instead:
// Instead of heterogeneous enum
enum Bad {
Yes = "YES",
No = 0,
}
// Better: Use union type
type YesNo = "YES" | "NO";
const Answer = {
Yes: "YES" as const,
No: "NO" as const,
};### Const Enums
Const enums are completely erased during compilation and cannot have computed members:
const enum Direction {
Up,
Down,
Left,
Right,
}
// This compiles to just the number value:
const dir = Direction.Up; // Becomes: const dir = 0;Const enums avoid runtime overhead but have limitations—they cannot reference other enums or use computed values.
### Reverse Mappings
Numeric enums create reverse mappings (number → name), but string enums do not:
enum NumericEnum {
A = 1,
B = 2,
}
console.log(NumericEnum[1]); // "A" - reverse mapping exists
enum StringEnum {
A = "VALUE_A",
B = "VALUE_B",
}
console.log(StringEnum["VALUE_A"]); // undefined - no reverse mappingThis is why string enums require explicit initializers—they don't participate in the auto-increment system.
### Alternatives to Enums
Modern TypeScript offers alternatives that avoid enum quirks:
// Object with const assertion (preferred in many cases)
const Status = {
Active: "ACTIVE",
Inactive: "INACTIVE",
Pending: "PENDING",
} as const;
type Status = typeof Status[keyof typeof Status];
// Type: "ACTIVE" | "INACTIVE" | "PENDING"This pattern gives you type safety without the enum initialization rules.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript