This article explores how union types and enums enhance type safety in TypeScript, preventing runtime errors and improving code maintainability.
In this article, we explore how union types and enums improve type safety in TypeScript. By limiting the values that can be assigned to specific properties, you can prevent potential runtime errors and improve maintainability. We’ll walk through examples, demonstrate common pitfalls, and show how to enforce stricter type checking.
Consider a scenario where a duck object is created with properties such as type and color. When these properties are represented as simple strings, TypeScript may accept unexpected values. For instance, using the following code:
Copy
Ask AI
land(): void { if (this.isFlying) { this.isFlying = false; console.log(`${this.name} lands gracefully`); } else { console.log(`${this.name} is already on the ground!`); }}const daffy = new PondDuck('Daffy', 3, 'Mallard', 'Black');const donald = new PondDuck('Donald', 5, 'Pekin', 'White');daffy.fly();daffy.fly();daffy.land();daffy.land();donald.fly();
The console output and a TypeScript error appear as follows:
Copy
Ask AI
[ERROR] 21:14:50 × Unable to compile TypeScript: index.ts(46,16): error TS2554: Expected 4-5 arguments, but got 1.[INFO] 21:15:03 Restarting: /root/code/index.ts has been modified[INFO] 21:15:16 Restarting: /root/code/index.ts has been modifiedDaffy starts flying!Daffy is already flying!Daffy lands gracefullyDaffy is already on the ground!Donald starts flying!
Here, because the class constructor accepts any string for duck type or color, passing a value like “Banana” (which doesn’t represent a valid duck color) might occur unintentionally.For example:
Copy
Ask AI
land(): void { if (this.isFlying) { this.isFlying = false; console.log(`${this.name} lands gracefully`); } else { console.log(`${this.name} is already on the ground!`); }}const daffy = new PondDuck('Daffy', 3, 'Mallard', 'Banana');const donald = new PondDuck('Donald', 5, 'Pekin', 'White');daffy.fly();daffy.fly();daffy.land();daffy.land();donald.fly();
Output:
Copy
Ask AI
Daffy is already flying!Daffy lands gracefullyDaffy is already on the ground!Donald starts flying![INFO] 21:19:55 Restarting: /root/code/index.ts has been modifiedDaffy starts flying!Daffy is already flying!Daffy lands gracefullyDaffy is already on the ground!Donald starts flying!
To tackle this issue, you can restrict the possible values for duck colors by creating a union type in TypeScript. For instance:
Copy
Ask AI
type DuckColor = 'White' | 'Brown' | 'Black' | 'Mixed';
This union type ensures that only the values ‘White’, ‘Brown’, ‘Black’, or ‘Mixed’ are permissible. If you attempt to assign a value like “Banana”, TypeScript will raise an error.
If “Banana” should be a valid color, simply add it to the union type:
Another effective approach is to use enums. Enums provide a set of named constants, making your code more expressive and less error-prone. Consider the following enum for duck types:
By using enums, you make it clear that only valid duck types (Mallard, Muscovy, or Pekin) are allowed. For example, to use the enum value, you would write:
Copy
Ask AI
const duckType = DuckType.Mallard;
This practice avoids ambiguity with numeric indexes that TypeScript might otherwise assign by default.
When creating instances of the class, the use of enums and union types enforces valid values. For example:
Copy
Ask AI
const daffy = new PondDuck('Daffy', 3, DuckType.Mallard, 'Black');const donald = new PondDuck('Donald', 5, DuckType.Pekin, 'White');daffy.fly();daffy.fly();daffy.land();daffy.land();donald.fly();
Attempting to pass an invalid duck color (e.g., “Banana”) will result in a compile-time error.
Daffy starts flying!Daffy is already flying!Daffy lands gracefully!Daffy is already on the ground!Donald starts flying!Mallard
Using union types and enums together ensures that only valid values are used for properties like duck type and duck color, making your code more robust and easier to maintain.
In this section, we’ve demonstrated how to leverage union types and enums to restrict property values in TypeScript. By enforcing valid values at compile time, you prevent bugs and improve code clarity. In our next article, we will explore additional advanced TypeScript features and their practical applications.