Overview
TypeScript Enums allow for a clearer and less error-prone way of dealing with sets of related constants. This tutorial takes you from basics to advanced techniques for transforming strings into Enums in TypeScript.
Introduction to Enums
In TypeScript, an enum is a way of giving more friendly names to sets of numeric values. Enums allow the developer to define a set of named constants, which makes the code easier to read and maintain. However, there are instances when we need to convert strings into an enum type. For example, when parsing server responses or user input, you might want to map received strings to an enumerated type to ensure type safety and code clarity. Now, let’s start simple.
Basic Enum Conversion
enum Status {
New,
InProgress,
Done,
Cancelled
}
function convertToEnum(value: string): Status | undefined {
return (Status as any)[value] || undefined;
}
// Usage:
let myStatus: Status = convertToEnum('InProgress'); // This will be Status.InProgress
This function attempts to match the string to a key in the ‘Status’ enum. If a match is found, it returns the enum value; otherwise, it returns ‘undefined’. This is suitable for enums where the keys are known and non-numeric.
Handling Numeric Enums
enum ErrorCodes {
NotFound = 404,
BadRequest = 400,
Unauthorized = 401
}
function stringToEnum(errorCode: string, enumeration: any): number | undefined {
for (let key in enumeration) {
if (enumeration[key] === errorCode) {
return parseInt(key);
}
}
return undefined;
}
// Usage:
const errorCode = stringToEnum('NotFound', ErrorCodes);
// This will be 404
This version iterates over an enum where values are numeric, comparing each with the given string. If a match is found, it returns the numeric equivalent of the enum key.
Strings as Enum Values
Sometimes you have enums with string values and you wish to perform a reverse lookup, from value to key. TypeScript doesn’t natively support reverse look-ups on enums with initialised string values. However, we can implement a custom function to handle this.
enum Directions {
North = 'NORTH',
South = 'SOUTH',
East = 'EAST',
West = 'WEST'
}
function stringToEnum(value: string, enumType: { [key: string]: string }): string | undefined {
for (let key in enumType) {
if (enumType[key] === value) {
return key;
}
}
return undefined;
}
// Usage:
let directionKey = stringToEnum('NORTH', Directions);
// Will be 'North'
Generic Approach
For better code reuse and type safety, it’s possible to define a generic converter function.
function stringToEnum<T extends { [index: string]: string }>(value: string, enumType: T): T[keyof T] | undefined {
let enumKeys = Object.keys(enumType).filter(x => enumType[x] == value);
if (enumKeys.length > 0) {
return enumType[enumKeys[0]] as T[keyof T];
}
return undefined;
}
// Usage:
let direction: Directions = stringToEnum('WEST', Directions);
// This will be Directions.West
This generic function narrows down the return type to only those types contained in the given enum, which provides better type checking and autocompletion.
Advanced Mapping and Error Handling
In real-world applications, user inputs and server responses may not always align perfectly with the enum naming conventions in your codebase. A common solution is to define a mapping object that ties strings to their respective enum values.
const DirectionMapping: { [key: string]: Directions } = {
'north-direction': Directions.North,
'south-direction': Directions.South,
// other mappings...
};
function advancedStringToEnum(value: string): Directions | never {
if (!DirectionMapping.hasOwnProperty(value)) {
throw new Error(`Value '${value}' cannot be converted to Directions enum`);
}
return DirectionMapping[value];
}
// Usage:
let myDirection = advancedStringToEnum('north-direction');
// This will be Directions.North
This allows for complex string mappings and provides clear runtime errors when conversions fail.
Conclusion
Converting strings to enums in TypeScript is a common but nuanced challenge. As our understanding of TypeScript deepens, the ability to write functions that convert data types becomes an important tool, ensuring our code remains robust and maintainable. Experimentation with the basic to advanced examples herein should provide a solid foundation for handling conversion in your own TypeScript projects.