Keywords: TypeScript | Enum | JavaScript Object | Bidirectional Mapping | Object.keys
Abstract: This article explores the implementation mechanism of TypeScript enums in JavaScript, explaining why direct use of Object.keys() returns mixed results and providing multiple methods to obtain pure enum values. By analyzing the compiled structure of enums, it details the bidirectional mapping characteristics of numeric and string keys, and presents complete code examples and performance comparisons for solutions using Object.keys().filter(), Object.values(), and other approaches.
Underlying Implementation Mechanism of TypeScript Enums
When TypeScript enums are compiled to JavaScript, they generate a special bidirectional mapping object. For the Color enum example:
export enum Color {
Red,
Green,
Blue,
}
The compiled JavaScript object structure is as follows:
{
'0': 'Red',
'1': 'Green',
'2': 'Blue',
Red: 0,
Green: 1,
Blue: 2
}
This design allows accessing enum names via numeric indices and accessing corresponding numeric values via enum names:
console.log(Color[0]); // "Red"
console.log(Color["Red"]); // 0
Solutions for Obtaining Pure Enum Values
Since enum objects contain both numeric and string keys, directly using Object.keys() returns all keys:
const allKeys = Object.keys(Color);
// Result: ['0', '1', '2', 'Red', 'Green', 'Blue']
Method 1: Using filter to Exclude Numeric Keys
The most common approach is to filter out numeric keys, retaining only string keys:
const enumValues = Object.keys(Color).filter((key) => {
return isNaN(Number(key));
});
// Result: ['Red', 'Green', 'Blue']
This method utilizes the isNaN() function to determine whether a key is numeric, based on the principle that numeric strings convert to numbers (not NaN), while enum names convert to NaN.
Method 2: Using Object.values() (TypeScript 3.1+)
For newer TypeScript versions, Object.values() combined with type assertions can be used:
const values = Object.values(Color).filter(
(value): value is string => typeof value === 'string'
);
// Result: ['Red', 'Green', 'Blue']
This approach is more concise but requires attention to TypeScript version compatibility.
Method 3: Using for...in Loop
Manually iterate through the enum object, selecting string keys:
const enumValues: string[] = [];
for (const key in Color) {
if (isNaN(Number(key))) {
enumValues.push(key);
}
}
// Result: ['Red', 'Green', 'Blue']
Performance Analysis and Best Practices
Regarding performance, the three methods have their own advantages and disadvantages:
- Filter Method: Concise code, good readability, suitable for most scenarios
- Object.values() Method: Modern JavaScript feature, better performance
- for...in Loop: Most flexible, capable of handling more complex filtering logic
It is recommended to choose the appropriate method based on TypeScript version and performance requirements in actual projects. For large enums, consider caching results to avoid repeated calculations.
Extended Application Scenarios
Methods for obtaining enum values can be applied to various scenarios:
- Dynamic Form Generation: Automatically generate dropdown options based on enum values
- Data Validation: Validate whether input values are valid enum values
- Internationalization: Retrieve corresponding localized text based on enum values
Example: Dynamically generating select box options
function createSelectOptions(enumObj: any): string {
const values = Object.keys(enumObj).filter(key => isNaN(Number(key)));
return values.map(value => `<option value="${value}">${value}</option>`).join('');
}
Conclusion
Understanding the underlying implementation of TypeScript enums is key to correctly retrieving enum values. By analyzing the compiled JavaScript object structure, we can choose appropriate methods to filter numeric keys and obtain arrays of pure enum names. In practical development, select the most suitable implementation based on project requirements and TypeScript version.