无法识别 Typescript 可区分的联合类型

Typescript discriminated union type not recognized

我正在尝试基于 https://github.com/ngrx/example-app/ 中的示例应用程序构建 Angular 2/ngrx 应用程序。我正在将动作类型导出为可区分的联合

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;

并将这些传递给 reducer 函数,根据类型 属性 进行区分,如下所示:

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

编译失败并出现错误(以及其他错误)

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'

我是做错了什么还是不了解受歧视工会的运作方式?我的理解是 switch 语句应该缩小 action.payload 的可能类型,以保证它是正确的类型。它似乎是在与 union 的所有成员的类型进行比较,而不是仅与具有匹配类型的成员进行比较。

该项目正在使用 Typescript v2.1.6

参见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.