vasily
vasily

Reputation: 2920

TypeScript and Union Types

Given: Reducer accepts one of the four actions:

interface ItemAction {
    type: 'ADD_TODO'|'DELETE_TODO'|'TOGGLE_TODO',
    id: number
}

interface QueryAction {
    type: 'SET_QUERY',
    query: string
}

I expect the code of the reducer contain something like:

if (action as ItemAction) {
   console.log(action.id); // typescript knows that "id" is available here
} else {
   console.log(action.query); // typescript knows that action is of type QueryAction and hence "query" is available here
}

How do I achieve that with TypeScript?

Upvotes: 1

Views: 64

Answers (2)

sam256
sam256

Reputation: 1421

This can be handled with a user-defined typeguard. For example

function isItemAction(someAction: any): someAction is ItemAction {
  if(someAction.id !== undefined and typeof someAction.id === 'number') {
    return true
  } else return false
}

if (if isItemAction(action)) {
  console.log(action.id); // typescript knows that "id" is available here
} 

A few things to know about typeguards: (1) they require the use of the type predicate "is" language; (2) they are conventionally named as I have named them, isX; (3) TS has no way of knowing that the evaluation logic in your function is accurate, that's up to you, in other words, a typeguard that always evaluates to true will be fine from TS's perspective.

See here for more info on writing user defined type guards.

Upvotes: 1

Etheryte
Etheryte

Reputation: 25310

The easiest solution is to use the in operator:

let action: ItemAction | QueryAction = ...;
if ('id' in action) {
  // Type inferred as ItemAction
} else {
  // Type inferred as QueryAction
}

For more complex cases, you can use type guards.

Upvotes: 2

Related Questions