Discriminated unions in TypeScript are a feature that enables the typing of structured data that can manifest in diverse forms, providing a versatile approach to handling varied types within a single entity.
These types share a property, known as the “discriminant”, which serves to distinguish variants between them. With this discriminator, we can precise type check the current of our multiple variations.
Common problem"undefined" anchor link
Commonly, we tend to create a type that covers all the variants.
Let’s explore this with an example:
type ApiResponse = { state: 'loading' | 'success' | 'error' isLoading?: true data?: string error?: { message: string }}
Now, we can type our object like this:
const mySuccessResponse: ApiResponse = { state: 'success', data: 'Our response data',}
But… the problem is that we are also able to do this:
const myErrorResponse: ApiResponse = { state: 'error', isLoading: true, data: 'Our response data',}
const mySuccessResponse: ApiResponse = { state: 'success', error: { message: 'Our error message', },}
Merging the three variants into a single type results in a loss of precise control over our data structure, as many properties become optional
.
So… how can we handle it accurately?
Using discriminated unions"undefined" anchor link
With discriminated unions, we can easily work with these types by using their shared property to differentiate them.
In this example, SuccessState
, LoadingState
and ErrorState
interfaces have a state
property acting as the discriminator.
type LoadingState = { state: 'loading' isLoading: true}
type SuccessState = { state: 'success' data: string}
type ErrorState = { state: 'error' error: { message: string }}
type ApiResponse = LoadingState | SuccessState | ErrorState
function getApiResponse(response: ApiResponse) { switch (response.state) { case 'loading': return response.isLoading case 'success': return response.data case 'error': return response.error.message }}
The switch statement based on response.state
allows TypeScript to infer the specific type within the union, enabling safe and structured code without unexpected errors.
In conclusion, discriminated unions in TypeScript simplify handling multiple variations of types based on a shared property.
This pattern, also know as “discriminating unions” enhances the scalability, while keeping the flexibility, reusability and fine-grained typing.
Happy coding! 🚀