In TypeScript, union types allow you to combine multiple types into one. This is particularly useful when a variable can hold values of different types. By using union types, you can create more flexible and robust type definitions that accurately represent the diverse data your application might encounter.
Union types are denoted by the pipe (|) symbol between the types you want to combine. For example, if a variable can be either a string or a number, you would define its type as string | number.
At its core, a union type is a way to express that a value could be one of several types. This feature is especially powerful when dealing with APIs that might return different data types based on certain conditions.
|) symbol to separate types.null or undefined to handle optional values.Let's explore some practical examples to understand how union types work and how they can be effectively utilized in TypeScript.
Suppose you have a function that accepts either a string or a number as an argument. You can define the parameter type using a union:
1function printValue(value: string | number) {2console.log(value);3}45printValue("Hello"); // Works6printValue(42); // Works7// printValue(true); // Error: Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
In this example, the value parameter can be either a string or a number. TypeScript ensures that any operations performed on value are valid for both types.
TypeScript's type inference and control flow analysis allow you to perform type-specific operations within your code. This is known as type narrowing:
1function formatValue(value: string | number) {2if (typeof value === "string") {3return value.toUpperCase();4} else {5return value.toFixed(2);6}7}89console.log(formatValue("hello")); // Outputs: HELLO10console.log(formatValue(123.456)); // Outputs: 123.46
Here, the typeof check narrows down the type of value, allowing you to call methods specific to strings or numbers.
Union types are commonly used with null or undefined to handle optional values:
1function greet(name: string | null) {2if (name === null) {3return "Hello, Guest!";4}5return `Hello, ${name}!`;6}78console.log(greet("Alice")); // Outputs: Hello, Alice!9console.log(greet(null)); // Outputs: Hello, Guest!
In this example, the greet function accepts a string or null. The check for null ensures that you handle both possible types correctly.
Union types can also be used to combine different object types. This is useful when dealing with data structures that might have varying shapes:
1interface Circle {2kind: "circle";3radius: number;4}56interface Square {7kind: "square";8sideLength: number;9}1011type Shape = Circle | Square;1213function area(shape: Shape): number {14switch (shape.kind) {15case "circle":16return Math.PI * shape.radius ** 2;17case "square":18return shape.sideLength ** 2;19default:20const _exhaustiveCheck: never = shape;21throw new Error("Unknown shape");22}23}2425const circle: Shape = { kind: "circle", radius: 5 };26const square: Shape = { kind: "square", sideLength: 10 };2728console.log(area(circle)); // Outputs: 78.5398163397448329console.log(area(square)); // Outputs: 100
In this advanced example, the Shape type is a union of Circle and Square. The area function uses a switch statement to handle different shapes based on their kind property. The _exhaustiveCheck ensures that all possible cases are handled, providing a safeguard against future changes.
Now that you have a solid understanding of union types, the next step is to explore intersection types. Intersection types allow you to combine multiple types into one, requiring that an object satisfy all the combined types. This feature provides another level of type flexibility and precision in your TypeScript applications.
Stay tuned for more advanced topics and keep enhancing your TypeScript skills!
Feel free to experiment with union types in your projects and see how they can make your code more robust and maintainable. Happy coding!