Skip to main content

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.