In this case, you can use a switch statement to narrow down which type is represented at runtime:

ts
type NetworkState = NetworkLoadingState | NetworkFailedState | NetworkSuccessState
 
function networkStatus(state: NetworkState): string {
// Right now TypeScript does not know which of the three
// potential types state could be.
 
// Trying to access a property which isn't shared
// across all types will raise an error
state.code
Property 'code' does not exist on type 'NetworkState'. Property 'code' does not exist on type 'NetworkLoadingState'.2339Property 'code' does not exist on type 'NetworkState'. Property 'code' does not exist on type 'NetworkLoadingState'.
 
// By switching on state, TypeScript can narrow the union
// down in code flow analysis
switch (state.state) {
case 'loading':
return 'Downloading...'
case 'failed':
// The type must be NetworkFailedState here,
// so accessing the `code` field is safe
return `Error ${state.code} downloading`
case 'success':
return `Downloaded ${state.response.title} - ${state.response.summary}`
}
}

Intersection Types

Intersection types are closely related to union types, but they are used very differently. An intersection type combines multiple types into one.