using-enums-and-literal-types-effectively
Using Enums and Literal Types Effectively:
1. Enums Introduction:
Enums, or enumerated types, allow you to define a set of named constants. Using enums can make it easier to document intent or create a set of distinct cases.
enum Direction {
Up,
Down,
Left,
Right
}
2. Numeric Enums:
Numeric enums are number-based enums where each member is assigned a number, starting from zero by default.
enum StatusCode {
NotFound = 404,
Success = 200,
BadRequest = 400,
Unauthorized = 401,
// ... and so on
}
3. String Enums:
String enums are similar to numeric enums, but the values are string literals instead of numbers.
enum FileExtension {
Json = ".json",
Txt = ".txt",
Csv = ".csv"
}
4. Enums at Runtime:
Enums are real objects that exist at runtime and can be iterated over or have their values retrieved.
enum Colors {
Red,
Green,
Blue
}
Object.values(Colors).forEach(color => console.log(color));
5. Const Enums for Performance:
Const enums are completely inlined at compile-time; their members are replaced with their constant values in the generated JavaScript.
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down];
6. Union Types with Enums:
Union types with enums restrict a variable to be one of the enum's values, providing a more specific type than just number
or string
.
enum LogLevel {
Error,
Warn,
Info,
Debug
}
function logMessage(level: LogLevel, message: string) {
// ...
}
logMessage(LogLevel.Error, "Error message"); // Correct
logMessage(10, "This will cause an error"); // TypeScript error
7. Literal Types for Specific Values:
Literal types allow you to specify the exact value a string or number must have.
type CardinalDirection = "North" | "East" | "South" | "West";
function move(distance: number, direction: CardinalDirection) {
// ...
}
move(5, "North"); // Correct
move(5, "Upwards"); // TypeScript error
With these examples, developers can understand how to use enums and literal types to improve the expressiveness and safety of their TypeScript code. Enums provide a way to handle a group of related values more efficiently, and literal types ensure that variables are assigned a specific value. 8. Literal Type Widening:
Literal type widening occurs when TypeScript expands a literal type to its base type, like 'string'. Using const assertions with as const
prevents TypeScript from widening the types.
let notWidened = "Hello, world!" as const; // Type is 'Hello, world!', not string
9. Combining Enums with Literal Types:
You can mix enums and literal types to define types that are flexible yet precise, specifying exactly what values are acceptable.
enum Pet {
Dog,
Cat
}
type PetName = "Max" | "Bella" | "Charlie";
function adoptPet(pet: Pet, name: PetName) {
// ...
}
10. Pattern Matching with Literal Types:
Literal types enable exhaustive checks in switch-case statements, ensuring that all possible cases are handled.
type Weather = "sunny" | "rainy" | "cloudy";
function dressForWeather(weather: Weather) {
switch (weather) {
case "sunny":
// dress code for sunny
break;
case "rainy":
// dress code for rainy
break;
case "cloudy":
// dress code for cloudy
break;
default:
const _exhaustiveCheck: never = weather;
return _exhaustiveCheck;
}
}
11. Enums for Code Organization:
Enums help organize code by grouping related constants, making the codebase easier to maintain.
enum UserRole {
Admin,
User,
Guest
}
12. Literal Types as Guards:
Literal types can serve as a form of type guard, ensuring certain values are used at runtime, which also assists TypeScript in type inference.
function processResponse(response: "success" | "failure") {
if (response === "success") {
// handle success
} else {
// handle failure
}
}
13. Refactoring with Enums and Literal Types:
Enums and literal types make it easier to refactor, as changes to these types are reflected throughout the codebase.
// Refactor by simply changing the enum
enum ApiEndpoint {
CreateUser = "/api/users/create",
DeleteUser = "/api/users/delete"
}
14. Enums vs Literal Types:
Choose between enums and literal types based on your needs: enums for a set of related constants, and literal types for specific, singular values.
// Enum for a category of items
enum Color {
Red,
Green,
Blue
}
// Literal type for a unique item
type PrimaryColor = "Red" | "Green" | "Blue";
15. Advanced Tip - Mapped Types with Literal Types:
Combine mapped types with literal types to create adaptable types based on rules, offering a powerful approach to type manipulation.
type Permissions = "read" | "write" | "execute";
type PermissionFlags = {
[K in Permissions]: boolean;
};
// Usage
const userPermissions: PermissionFlags = {
read: true,
write: false,
execute: true
};
By utilizing these features, you can enhance type safety and clarity in your TypeScript code, benefiting from the robust type system TypeScript offers.