Real-World Union Narrowing Scenarios
123456789101112131415161718192021222324252627282930313233343536// Suppose you receive responses from an API that can return either a User, an Error, or a Loading state. type UserResponse = | { status: "success"; user: { id: number; name: string } } | { status: "error"; message: string } | { status: "loading" }; // A function to handle the API response safely: function handleApiResponse(response: UserResponse) { // Safe narrowing using discriminant property 'status' if (response.status === "success") { // TypeScript knows response is { status: "success"; user: ... } console.log("User ID:", response.user.id); console.log("User Name:", response.user.name); } else if (response.status === "error") { // TypeScript knows response is { status: "error"; message: ... } console.error("API Error:", response.message); } else if (response.status === "loading") { // TypeScript knows response is { status: "loading" } console.log("Loading user data..."); } else { // This should never happen if all cases are handled // (optionally use assertNever for exhaustive checking) throw new Error("Unhandled response status"); } } // Dangers of unsafe assertion: function unsafeHandleApiResponse(response: UserResponse) { // This assertion ignores the real structure at runtime! // If response is not actually a success, this will throw at runtime. const user = (response as { user: { id: number; name: string } }).user; // If response did not have a 'user' property, this will be undefined or throw. console.log("User ID:", user.id); console.log("User Name:", user.name); }
When you process data from external sources such as APIs, you often deal with union types representing multiple possible outcomes. Using type guards and discriminant properties, you can safely determine the actual shape of the data at runtime. This approach avoids dangerous assumptions and ensures that only valid operations are performed on each possible type.
By checking the status property, you allow TypeScript to narrow the type of the response, enabling safe access to properties like user or message. This prevents runtime errors that would occur if you tried to access a property that does not exist on the current variant of the union.
In contrast, using an unsafe as Type assertion tells TypeScript to trust you, even if the value does not actually match the asserted type. This can lead to undefined values, runtime exceptions, and bugs that are difficult to trace. Relying on safe narrowing techniques not only prevents these errors but also improves code maintainability, as refactoring and extending union types becomes safer and more predictable. By avoiding unsafe assertions, you ensure your code remains robust as the shape of your data evolves.
¡Gracias por tus comentarios!
Pregunte a AI
Pregunte a AI
Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla
Awesome!
Completion rate improved to 5.88
Real-World Union Narrowing Scenarios
Desliza para mostrar el menú
123456789101112131415161718192021222324252627282930313233343536// Suppose you receive responses from an API that can return either a User, an Error, or a Loading state. type UserResponse = | { status: "success"; user: { id: number; name: string } } | { status: "error"; message: string } | { status: "loading" }; // A function to handle the API response safely: function handleApiResponse(response: UserResponse) { // Safe narrowing using discriminant property 'status' if (response.status === "success") { // TypeScript knows response is { status: "success"; user: ... } console.log("User ID:", response.user.id); console.log("User Name:", response.user.name); } else if (response.status === "error") { // TypeScript knows response is { status: "error"; message: ... } console.error("API Error:", response.message); } else if (response.status === "loading") { // TypeScript knows response is { status: "loading" } console.log("Loading user data..."); } else { // This should never happen if all cases are handled // (optionally use assertNever for exhaustive checking) throw new Error("Unhandled response status"); } } // Dangers of unsafe assertion: function unsafeHandleApiResponse(response: UserResponse) { // This assertion ignores the real structure at runtime! // If response is not actually a success, this will throw at runtime. const user = (response as { user: { id: number; name: string } }).user; // If response did not have a 'user' property, this will be undefined or throw. console.log("User ID:", user.id); console.log("User Name:", user.name); }
When you process data from external sources such as APIs, you often deal with union types representing multiple possible outcomes. Using type guards and discriminant properties, you can safely determine the actual shape of the data at runtime. This approach avoids dangerous assumptions and ensures that only valid operations are performed on each possible type.
By checking the status property, you allow TypeScript to narrow the type of the response, enabling safe access to properties like user or message. This prevents runtime errors that would occur if you tried to access a property that does not exist on the current variant of the union.
In contrast, using an unsafe as Type assertion tells TypeScript to trust you, even if the value does not actually match the asserted type. This can lead to undefined values, runtime exceptions, and bugs that are difficult to trace. Relying on safe narrowing techniques not only prevents these errors but also improves code maintainability, as refactoring and extending union types becomes safer and more predictable. By avoiding unsafe assertions, you ensure your code remains robust as the shape of your data evolves.
¡Gracias por tus comentarios!