Typescript无法破坏联合类型

问题描述 投票:1回答:3

我有一个联合类型Actions

type Actions = Readonly<{
    type: ActionTypes.LOAD_POST;
    payload: string;
}> | Readonly<{
    type: ActionTypes.LOAD_POST_FAIL;
    payload: string;
}> | Readonly<{
    type: ActionTypes.LOAD_POST_SUCCESS;
    payload: {
        url: string;
        post: Post;
    };
}>

(这是生成的类型,原始版本嵌套了多个类型和ReturnType。)ActionTypes是一个字符串枚举。

const postReducer = (state = initialPostState, action: Actions): PostState => {
  const { type, payload } = action;
  switch (action.type) {
    case ActionTypes.LOAD_POST_SUCCESS: {
      const { post } = action.payload; // No error
      return { ...state, loading: false, success: true, post };
    }
  }

  switch (type) {
    case ActionTypes.LOAD_POST: {
      return { ...state, loading: true };
    }
    case ActionTypes.LOAD_POST_SUCCESS: {
      // [ts] Type 'string | { url: string; post: IFullPost; }' has no property 'post' and no string index signature.
      const { post } = payload;
      return { ...state, loading: false, success: true, post };
    }
    case ActionTypes.LOAD_POST_FAIL: {
      return { ...state, loading: false, success: false, post: null };
    }
    default:
      return state;
  }
};

为什么第一个工作而不是第二个?

typescript typescript-typings typescript2.0
3个回答
0
投票

您必须打开action.type才能让action.payload在case语句中更改其类型。


0
投票

这是设计的。这是一个非常简单的例子:

type Actions =
    {
        type: 1,
        payload: string;
    } |
    {
        type: 2,
        payload: { a: string }
    }

function r(action: Actions) {
    const { type } = action;
    switch (type) {
        case 2: {
            // Type 'string | { a: string; }' has no property 'a' and no string index signature.
            const { a } = action.payload;
        }

    }
}

当我们破坏action对象:const { type, payload } = action;时,我们丢失了被破坏类型的耦合信息。在此之后,type常数将具有1 | 2类型,payload将具有string | { a: string; },即每种类型将基于Actions类型结合所有可能的选项。这就是为什么TS无法弄清楚payload的确切类型,因为在switch条件下我们有绝对独立的变量。


0
投票

您正在体验TypeScript达到type guards类型推断的极限。

在你的非工作示例中,TypeScript无法推断有关已经被破坏的变量payload的任何信息,尽管它在技术上是可行的。我猜类型防护只能直接/字面上涉及防护表达的对象。

© www.soinside.com 2019 - 2024. All rights reserved.