Typescript Enums And Literal Types Complete Guide
Understanding the Core Concepts of TypeScript Enums and Literal Types
TypeScript Enums and Literal Types: A Comprehensive Guide
Enumerations (Enums)
Enums in TypeScript are a feature that allows you to define a set of named constants. Enums are useful for providing readable names to sets of numeric values or string values. Enums help in making your code more organized, and maintainable by reducing the risk of errors.
Numeric Enums:
By default, the enum
members in TypeScript are given numeric values starting at 0 and incrementing in one step. However, you can change the default behavior by explicitly setting the value for each member.
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
const currentDirection: Direction = Direction.Left;
console.log(currentDirection); // Prints: 2
You can also assign specific values to each member:
enum Direction {
Up = 1,
Down,
Left = 10,
Right
}
console.log(Direction.Right); // Prints: 11
String Enums:
Using string values for enum members increases the readability and maintainability of your code since the enum member names will appear in the generated JavaScript.
enum Directions {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
console.log(Directions.Up); // Prints: 'UP'
Reverse Mappings:
In numeric enums, TypeScript generates a reverse mapping, which allows you to lookup the names of the enum members from their values.
let directionName: string = Direction[2];
console.log(directionName); // Prints: 'Left'
Const Enums:
When you use the const
keyword, the compiler generates more efficient code since it removes the enum definition and replaces all usages with the inline values. This helps in reducing the size of the generated code.
const enum Colors {
Red,
Green,
Blue
}
const color: number = Colors.Green;
In the above example, the generated JavaScript won't define a Colors
object. Instead, it will replace Colors.Green
with 1
directly.
Literal Types
Literal Types restrict a variable to be a certain value(s). They are often used in conjunction with Union Types to define a set of values that a variable can hold. Literal types are useful for defining constants and enhancing the accuracy of types.
String Literal Types:
You can define a variable that can hold a specific set of string values.
type DirectionLiteral = 'up' | 'down' | 'left' | 'right';
const direction: DirectionLiteral = 'up';
Numeric Literal Types:
Similar to string literals, you can also define numeric literals.
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
const diceRoll: DiceRoll = 4;
Boolean Literal Types:
Using boolean literal types, you can make a variable hold only true
or false
.
type TrueOrFalse = true | false;
const isCorrect: TrueOrFalse = true;
Combining Literals with Other Types:
You can mix literal types with other types to create more expressive types.
type MessageId = 'success' | 'error' | 'warning';
interface UserMessage {
messageId: MessageId;
message: string;
}
const successMessage: UserMessage = {
messageId: 'success',
message: 'Action completed successfully'
};
Practical Use Cases
Configuration Values: Defining enums for configuration constants makes the code more readable and self-documenting.
Action Types in Redux: Enums can be used to define action types in Redux, making it less error-prone to dispatch actions.
Error Codes: Defining error codes as enums can make it easier to handle different types of errors in a program.
Literal Types for Constants: Using literal types for constants ensures that the variable cannot accidentally hold an invalid value.
Conclusion
TypeScript's enums and literal types offer developers a powerful way to ensure the type safety and clarity of their code. By using enums to define named constants and literal types to restrict the values a variable can hold, you can build more robust applications while reducing the likelihood of errors due to invalid data types.
Online Code run
Step-by-Step Guide: How to Implement TypeScript Enums and Literal Types
Complete Examples, Step by Step for Beginners: TypeScript Enums and Literal Types
Understanding Enums in TypeScript
Enums (short for enumerations) are a way to define a set of named constants that can be used throughout your codebase. This feature helps in making your code more readable and maintainable.
Step 1: Defining a Simple Enum
Let's create an enum called Direction
that will contain values for the four cardinal directions.
enum Direction {
North,
East,
South,
West
}
console.log(Direction.North); // Output: 0
console.log(Direction.East); // Output: 1
console.log(Direction.South); // Output: 2
console.log(Direction.West); // Output: 3
By default, TypeScript assigns numeric values to the enum members, starting from 0.
Step 2: Setting Numeric Values for Enum Members
We can also assign specific numeric values to each member of the enum:
enum Direction {
North = 1,
East = 2,
South = 3,
West = 4
}
console.log(Direction.North); // Output: 1
console.log(Direction.East); // Output: 2
console.log(Direction.South); // Output: 3
console.log(Direction.West); // Output: 4
Step 3: Using String Enums
In addition to numeric enums, TypeScript also supports string enums where each value is a string literal:
enum Direction {
North = "NORTH",
East = "EAST",
South = "SOUTH",
West = "WEST"
}
console.log(Direction.North); // Output: "NORTH"
console.log(Direction.East); // Output: "EAST"
console.log(Direction.South); // Output: "SOUTH"
console.log(Direction.West); // Output: "WEST"
Step 4: Getting Keys from Enums
Sometimes, you may need to get all the keys from an enum:
const directions = Object.keys(Direction);
console.log(directions);
// Output: [ 'North', 'East', 'South', 'West' ] for numeric enum
// Output: ['North', 'East', 'South', 'West'] for string enum
// For numeric enums, Object.values() might include the keys and values:
const directionValues = Object.values(Direction);
console.log(directionValues);
// Output: ["North", 0, "East", 1, "South", 2, "West", 3] for numeric enum
// Output: [ 'NORTH', 'EAST', 'SOUTH', 'WEST' ] for string enum
Step 5: Using Enums as Parameters
Here’s how to use an enum as a parameter in a function:
enum Action {
Submit,
Cancel,
Reset
}
function handleAction(action: Action): string {
switch(action) {
case Action.Submit: return "Action Submitted!";
case Action.Cancel: return "Action Cancelled!";
case Action.Reset: return "Action Reset!";
default: return "Unknown action!";
}
}
console.log(handleAction(Action.Submit)); // Output: "Action Submitted!"
Understanding Literal Types in TypeScript
Literal types allow you to specify that a variable or function parameter must have a very specific, hardcoded value. They are useful to make your code more explicit.
Step 1: Defining String Literal Types
String literal types ensure that the variable can only hold specific string values.
type DirectionLiteral = 'north' | 'east' | 'south' | 'west';
function moveTo(direction: DirectionLiteral): void {
console.log(`Moving to ${direction}`);
}
moveTo('north'); // Correct
moveTo('up'); // Error: Argument of type '"up"' is not assignable to parameter of type 'DirectionLiteral'
Step 2: Defining Numeric Literal Types
Numeric literal types are similar to string literal types but they allow numeric values instead.
type PossibleScores = 0 | 5 | 10 | 15;
function awardScore(points: PossibleScores): void {
console.log(`Awarding ${points} points`);
}
awardScore(5);
awardScore(3); // Error: Argument of type '3' is not assignable to parameter of type 'PossibleScores'
Step 3: Combining Enums with Literal Types
You can combine enums with literal types to make sure that a variable or parameter holds one of the values from the enum:
enum Color {
Red,
Green,
Blue
}
type ColorLiteral = "Red" | "Green" | "Blue";
function paint(color: ColorLiteral): void {
console.log(`Painting in color ${color}`);
}
paint(Color.Red.toString()); // Correct, converting enum value to string first
paint("Red"); // Correct
paint("Yellow"); // Error: Argument of type '"Yellow"' is not assignable to parameter of type 'ColorLiteral'
Step 4: Using Literal Types for Function Return Values
You can also restrict the allowed return values of a function using literal types.
type YesOrNo = 'Yes' | 'No';
function willAttend(event: string): YesOrNo {
if (event === 'wedding') {
return 'Yes';
} else {
return 'No';
}
}
console.log(willAttend('wedding')); // Output: 'Yes'
console.log(willAttend('meeting')); // Output: 'No'
console.log(willAttend('concert')); // No error (returns 'Yes' or 'No')
Practical Exercise: Building a Game Control System
Now, let’s use enums and literal types together to build a simple game control system.
Define Enums for Movement and Actions
enum Movement {
MoveLeft = 'MOVE_LEFT',
MoveRight = 'MOVE_RIGHT',
MoveForward = 'MOVE_FORWARD',
MoveBackward = 'MOVE_BACKWARD'
}
enum Action {
Shoot = 'SHOOT',
Jump = 'JUMP',
Duck = 'DUCK',
Crouch = 'CROUCH'
}
Create a Type that Combines Movement and Action
type ControlCommand = Movement.Shoot | Movement.Jump | Movement.Duck | Movement.Crouch
| Action.MoveLeft | Action.MoveRight | Action.MoveForward | Action.MoveBackward;
Define the performCommand
Function
This function will execute a given command if it matches any of the enum values combined.
function performCommand(command: ControlCommand): string {
switch(command) {
case Movement.MoveLeft:
return "Moving left...";
case Movement.MoveRight:
return "Moving right...";
case Movement.MoveForward:
return "Moving forward...";
case Movement.MoveBackward:
return "Moving backward...";
case Action.Shoot:
return "Shooting!";
case Action.Jump:
return "Jumping!";
case Action.Duck:
return "Ducking!";
case Action.Crouch:
return "Crouching!";
default:
return "Unknown command!" // This line won't be triggered due to ControlCommand type restriction
}
}
Testing the System
Top 10 Interview Questions & Answers on TypeScript Enums and Literal Types
1. What are Enums in TypeScript?
Answer: Enums (short for enumerations) in TypeScript are a way to define a set of named constants. They allow you to define a set of related values and refer to them by a name instead of using magic numbers or strings. This improves code readability and maintainability.
Example:
enum Day {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
2. How can you start an Enum with a different number other than 0?
Answer: By default, the first value of an Enum starts with 0. You can start it at a different number by initializing the first member with a specific value.
Example:
enum Day {
Sunday = 1,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
Here, Day.Sunday
will be 1, Day.Monday
will be 2, and so on.
3. What are String Enums in TypeScript?
Answer: String Enums in TypeScript allow you to assign string values to members instead of the default numeric ones. This can make debugging and serialization easier.
Example:
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
4. What are Literal Types in TypeScript?
Answer: Literal Types in TypeScript refer to the type of specific values. They are particularly useful when combined with unions to create fixed sets of values.
Example:
type Direction = "north" | "south" | "east" | "west";
let myDirection: Direction = "north";
// myDirection can only be one of the four values specified
5. How can Enums and Literal Types be used together?
Answer: Enums and Literal Types can sometimes serve similar purposes, but they are used in different contexts. Enums are often used when you have a predefined set of constants, usually numbers or strings. Literal Types are used when you want to restrict a variable to a small set of specific values, especially when these values are known at compile time and are strings or numbers.
Example:
enum Status {
Active = "active",
Inactive = "inactive",
Pending = "pending"
}
type Action = {
type: "activate" | "deactivate" | "pending";
}
function handleAction(action: Action) {
// Implementation based on the action type
}
6. Why use Enums over Literal Types?
Answer: Enums provide a way to group related constants and use them like a namespace, which can make the code more organized. They also offer better tooling support, such as auto-completion in editors.
Literal Types are more flexible when used with unions, as they allow defining a variable that can only take one of the specified values. They can also be used for more complex type definitions not easily represented with enums.
7. How can you use Enums as a type?
Answer: Enums in TypeScript not only define a set of values but also define a type that can be used in type annotations.
Example:
enum Color {
Red,
Green,
Blue
}
function printColor(color: Color) {
console.log(color);
}
printColor(Color.Red); // Valid
printColor(0); // Valid, as it maps to Color.Red
printColor(99); // Compile error, as it does not have a mapping in Color
8. What are Reverse Mappings in TypeScript Enums?
Answer: Reverse mappings in TypeScript Enums allow you to access the Enum values by their numeric values, not just by their names. This can be useful for debugging or when you have a numeric value and want to find out its Enum name.
Example:
enum Color {
Red = 1,
Green,
Blue,
}
console.log(Color.Red); // Outputs: 1
console.log(Color[1]); // Outputs: "Red"
9. Can you have const Enums in TypeScript?
Answer:
Yes, you can have const
enums in TypeScript. Unlike regular enums, const enums do not generate any JavaScript code for the enum itself; instead, the compiler will inline the enum's values at compile time. This can result in smaller and more performant code.
Example:
const enum Color {
Red,
Green,
Blue,
}
let myColor = Color.Red; // Will be compiled to 'let myColor = 0;'
10. What are some use cases for Literal Types and Enums?
Answer:
Enums:
- Defining a set of fixed constants that represent a concept (e.g., days of the week).
- Mapping HTTP status codes.
- Creating a set of configuration options.
Literal Types:
- Restricting function parameters to specific values (useful for API calls, states, etc.).
- Creating type-safe configurations.
- Defining action types for use in reducers in state management libraries like Redux.
Login to post a comment.