Here is the link for the firs part Conditional Typing in TypeScript
Table of contents
Open Table of contents
The Prolem
Imagine you have an APIResponse
type that accommodates loading, success and error responses.
type APIResponse = {
state: "loading" | "success" | "error";
data?: { name: string; age: number };
errorMessage?: string;
};
The APIResponse
type attempts to cover loading, success, and error states within a single type. This leads to ambiguity and errors, making it challenging to write robust TypeScript code.
Using Discriminating Union
In TypeScript, discriminated unions are like a superhero move, letting developers create types that can handle different situations. They use discriminators, little markers for each situation, to make sure everything is managed smoothly. However, there’s a catch explored in the script: discriminated unions can only work their magic with specific values, not with generic types. So, while these unions are superheroic in handling known situations, they might need a sidekick when it comes to dealing with more generic stuff in TypeScript.
We can convert the APIResponse
type into a discriminated union using the following approach
type LoadingState = {
state: "loading";
};
type SuccessState = {
state: "success";
data: { name: string; age: number };
};
type ErrorState = {
state: "error";
errorMessage: string;
};
type APIResponse = LoadingState | SuccessState | ErrorState;
Now, each possible state of the API response has its own type, making it a discriminated union. The state property serves as the discriminator, ensuring that TypeScript can infer and check the specific shape of the object based on its state value.
Than we can create handleApiResponse
function that takes an APIResponse
as a parameter and uses a switch case to handle different states accordingly.
function handleApiResponse(response: APIResponse): void {
switch (response.state) {
case "loading":
console.log("Loading...");
break;
case "success":
console.log("Success! Data:", response.data);
break;
case "error":
console.error("Error:", response.errorMessage);
break;
default:
// Handle unexpected state
console.error("Unexpected state:", response);
}
}
Conclusion
- Separate Type Definitions: Consider defining complex types separately for better code readability and maintainability.
- Use Type Guards: Employ type guards to access conditional properties and guide TypeScript’s inference.
- Type Composition: Leverage the power of type composition using the & and | operators to create expressive and conditional types.
By mastering conditional TypeScript types, you can significantly enhance your code’s type safety and expressiveness, making it a valuable skill in your TypeScript toolkit.