Keywords: TypeScript | Enum Iteration | Numeric Enum | String Enum | Object.keys | For Loop
Abstract: This article provides an in-depth exploration of enum value iteration in TypeScript, analyzing the different behaviors of numeric and string enums, and offering multiple practical iteration solutions. Through concrete code examples and performance comparisons, it helps developers master the core concepts and best practices of enum iteration, addressing common issues encountered in real-world development.
Basic Concepts of Enum Iteration
In TypeScript development, enums are a commonly used data type for defining a set of named constants. However, many developers encounter unexpected behavior when iterating over enum values, particularly with numeric enums that return both key names and numeric indices.
The Problem with Numeric Enum Iteration
Consider the following numeric enum definition:
export enum MotifIntervention {
Intrusion,
Identification,
AbsenceTest,
Autre
}
When using a for...in loop to iterate over this enum:
for (let motif in MotifIntervention) {
console.log(motif);
}
The output includes both key names and numeric indices:
0
1
2
3
Intrusion
Identification
AbsenceTest
Autre
This occurs because TypeScript generates bidirectional mappings when compiling numeric enums, including both name-to-value and value-to-name mappings.
Solution 1: Filtering Numeric Indices
The first solution uses the isNaN function to filter out numeric indices:
for (let item in MotifIntervention) {
if (isNaN(Number(item))) {
console.log(item);
}
}
This approach filters out numeric indices by checking whether each key is a number, retaining only the enum member names.
Solution 2: Using Object.keys and filter
The second solution combines Object.keys and filter methods:
Object.keys(MotifIntervention).filter(key => !isNaN(Number(MotifIntervention[key])));
This method first retrieves all keys of the enum, then filters out those keys whose corresponding values are numbers, resulting in a pure array of enum member names.
String Enum Iteration
Unlike numeric enums, string enums do not generate reverse mappings during compilation:
enum MyEnum {
A = "a",
B = "b",
C = "c"
}
The compiled JavaScript code is:
var MyEnum;
(function (MyEnum) {
MyEnum["A"] = "a";
MyEnum["B"] = "b";
MyEnum["C"] = "c";
})(MyEnum || (MyEnum = {}));
This generates an object structure of:
{
A: "a",
B: "b",
C: "c"
}
Iterating Over String Enum Keys
To get all keys of a string enum:
Object.keys(MyEnum);
Returns: ["A", "B", "C"]
Iterating Over String Enum Values
To get all values of a string enum:
Object.keys(MyEnum).map(key => MyEnum[key])
Or using the more concise Object.values:
Object.values(MyEnum)
Both methods return: ["a", "b", "c"]
Performance Considerations and Best Practices
In performance-sensitive scenarios, using for...of loops to iterate over Object.values results is generally more efficient than functional approaches:
for (const value of Object.values(MyEnum)) {
console.log(value);
}
String enums have advantages over string union types in terms of runtime iteration capability. If you need to dynamically retrieve all possible values at runtime, enums are the better choice.
Practical Application Scenarios
In frontend frameworks like Angular, enum iteration is commonly used to generate dynamic form controls, such as radio button groups:
export class InterventionDetails implements OnInit {
motifs = Object.keys(MotifIntervention)
.filter(key => isNaN(Number(key)));
constructor(private interService: InterventionService) {}
}
This allows direct use of the motifs array in templates to render radio buttons.
Conclusion
The key to TypeScript enum iteration lies in understanding the differences in compilation behavior between different types of enums. Numeric enums require additional filtering steps to remove numeric indices, while string enums can be iterated using standard object iteration methods. Choosing the appropriate iteration strategy based on specific requirements enables writing clearer and more efficient code.