简体   繁体   中英

Typescript discriminated union type not recognized

I am trying to build an Angular 2/ngrx application based on the example app found at https://github.com/ngrx/example-app/ . I am exporting action types as a discriminated union

export const ActionTypes = {
  QUERY: type('[Games] Query'),
  QUERY_COMPLETE: type('[Games] Query Complete'),
  INVALIDATE: type('[Games] Invalidate'),
  SELECT: type('[Games] Select'),
  LOAD_NEXT_PAGE: type('[Games] Load Next Page'),
  LOAD_NEXT_PAGE_COMPLETE: type('[Games] Load Next Page Complete'),

};

export class QueryAction implements Action {
    type = ActionTypes.QUERY;

    constructor(public payload: string) {}
}

export class QueryCompleteAction implements Action {
    type = ActionTypes.QUERY_COMPLETE;

    constructor(public payload: Game[]) {}

}

export class LoadNextPageCompleteAction implements Action {
    type = ActionTypes.LOAD_NEXT_PAGE_COMPLETE;

    constructor(public payload: Game[]) {}
}

export class LoadNextPageAction implements Action {
    type = ActionTypes.LOAD_NEXT_PAGE;

    constructor() {}
}

export class InvalidateAction implements Action {
    type = ActionTypes.INVALIDATE;

    constructor(){}
}

export class SelectAction implements Action {
    type = ActionTypes.SELECT;

    constructor(public payload: number) {}
}

export type Actions = QueryAction | QueryCompleteAction | InvalidateAction | SelectAction | LoadNextPageAction | LoadNextPageCompleteAction;

And passing these to the reducer function, discriminating based on the type property as follows:

export function reducer(state = initialState, action: game.Actions): State {
  switch (action.type) {
    case game.ActionTypes.LOAD_NEXT_PAGE:
    case game.ActionTypes.QUERY: {
      return Object.assign({}, state, {loading: true});
    }
    case game.ActionTypes.QUERY_COMPLETE: {
      return {
        games: action.payload,
        offset: action.payload.length,
        loading: false,
        selectedGameId: null
      }
    }
    case game.ActionTypes.LOAD_NEXT_PAGE_COMPLETE: {
      return {
        games: [...state.games, ...action.payload],
        offset: state.offset + action.payload.length,
        loading: false,
        selectedGameId: state.selectedGameId
      }
    }
    case game.ActionTypes.SELECT: {
      return Object.assign({}, state, {selectedGameId: action.payload});
    }
    default: {
      return state;
    }
  }
}

This fails to compile with the error (among other errors)

Type 'string | number | Game[]' is not assignable to type 'Game[]'.
Type 'string' is not assignable to type 'Game[]'.
Property 'length' does not exist on type 'string | number | Game[]'.
Property 'length' does not exist on type 'number'

Am I doing something wrong or not understanding how discriminated unions work? My understanding was that the switch statement should narrow down the possible types of action.payload so it is guaranteed to be the correct type. It seems to be comparing to the types of all of the members of the union, instead of only the one with the matched type.

The project is using Typescript v2.1.6

See this github issue and pull request .

Starting with breaking changes in TypeScript 2.1+, string literal types are always widened to strings unless assigned to immutable const variables or readonly properties. This means things like switch (action.type) statements leveraging discriminated unions stop working with the current example-app patterns.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM