Búsqueda de sitios web

ngrx - Tipo de unión discriminada de Typescript no reconocido


Estoy tratando de construir una aplicación Angular 2/ngrx basada en la aplicación de ejemplo que se encuentra en https://github.com/ngrx/example-app/. Estoy exportando tipos de acción como una unión discriminada

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;

Y pasando estos a la función reductora, discriminando según la propiedad tipo de la siguiente manera:

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;
    }
  }
}

Esto no se compila con el error (entre otros errores)

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'

¿Estoy haciendo algo mal o no entendiendo cómo funcionan las uniones discriminadas? Mi entendimiento era que la sentencia switch debería reducir los posibles tipos de action.payload para que se garantice que sea del tipo correcto. Parece que está comparando con los tipos de todos los miembros de la unión, en lugar de solo con aquel cuyo tipo coincide.

El proyecto está usando Typescript v2.1.6

Respuesta #1

Ve este problema y solicitud de extracción en GitHub.

Empezando con los cambios incompatibles en TypeScript 2.1+, los tipos literales de cadena siempre se amplían a cadenas a menos que se asignen a variables const inmutables o propiedades readonly. Esto significa que cosas como las declaraciones switch (action.type) que aprovechan uniones discriminadas dejan de funcionar con los patrones actuales de la aplicación de ejemplo.