Property 'loginToken' does not exist on type '{ loginToken: string; } | { error: Error; } | { username: string; password: string; }'

Property 'loginToken' does not exist on type '{ loginToken: string; } | { error: Error; } | { username: string; password: string; }'

我正在尝试使用 typescript/redux 创建一个 reducer,但我遇到了这个错误

Property 'loginToken' does not exist on type '{ loginToken: string; } | { error: Error; } | { username: string; password: string; }'.
  Property 'loginToken' does not exist on type '{ error: Error; }'

我的reducer如下:

export default (state = userState, action: UsersActions) => {
  switch (action.type) {
    case ActionTypes.USER_LOGIN_REQUEST:
      return {
        ...state,
        isAuthenticated: false,
        isLoading: true,
      };
    case ActionTypes.USER_LOGIN_SUCCESS:
      return {
        ...state,
        isAuthenticated: true,
        loginToken: action.payload.loginToken,
        isLoading: false,
      };

我的动作类型:

export interface UserLoginRequest extends Action {
  type: typeof ActionTypes.USER_LOGIN_REQUEST; // !! here we assign the string literal type of the constant
  payload: {
    username: string;
    password: string;
  };
}

export interface UserLoginSuccess extends Action {
  type: typeof ActionTypes.USER_LOGIN_SUCCESS; // !! here we assign the string literal type of the constant
  payload: {
    loginToken: string;
  };
}

export interface UserLoginFailure extends Action {
  type: typeof ActionTypes.USER_LOGIN_FAILURE; // !! here we assign the string literal type of the constant
  payload: {
    error: Error;
  };
}

export interface User {
  username: string;
  password: string;
  loginToken: string;
  loginError?: Error;
  status?: number;
}

export type UsersActions = 
| UserLoginSuccess
| UserLoginFailure
| UserLoginRequest;

操作类型是这样的枚举:

enum ActionTypes {
    USER_LOGIN_REQUEST = 'request',
    USER_LOGIN_SUCCESS = 'success',
    USER_LOGIN_FAILURE = 'failure',
};

也许我没有得到有关 Typescript 的信息,这就是为什么?如果可以提供任何资源或见解,我们将不胜感激。

问题是 UsersActions 可以是三种情况之一 - UserLoginSuccess、UserLoginFailure 或 UserLoginRequest - 它是联合类型。

在“成功”的情况下,知道登录令牌将存在,并且类型是 UserLoginSuccess - 但 TypeScript 没有任何方式知道这一点,所以它告诉您您可能正在尝试从(例如)UserLoginFailure 类型中获取 loginToken 属性。

解决办法是you've got to narrow the type when you want to work with it。由于联合类型上没有可以打开的公共属性,我能想到的唯一方法是将有效负载转换为正确的类型:

case ActionTypes.USER_LOGIN_SUCCESS:
  return {
    ...state,
    isAuthenticated: true,
    loginToken: (action.payload as UserLoginSuccess).loginToken,
    isLoading: false,
  };

编辑

抱歉,我刚刚重新阅读了您的问题并注意到有更好的解决方案 - 当您创建联合类型时,您使用 typeof 作为操作类型 -

export interface UserLoginRequest extends Action {
  type: typeof ActionTypes.USER_LOGIN_REQUEST;
  // ...
}

所以 type 将是 属性 的类型 - 但我猜你真正想做的是将其变成已知值?

我不知道 ActionsTypes 的定义是什么,但如果这是 enum -

enum ActionTypes {
    USER_LOGIN_REQUEST = 'request',
    USER_LOGIN_SUCCESS = 'success',
    USER_LOGIN_FAILURE = 'failure',
};

然后您可以更改类型定义以改用枚举类型 -

interface UserLoginRequest extends Action {
  type: ActionTypes.USER_LOGIN_REQUEST;
  payload: {
    username: string;
    password: string;
  };
}

现在,当您打开 type 时,TS 会正确地为您缩小负载 -

switch (action.type) {
    case ActionTypes.USER_LOGIN_SUCCESS:
        console.log(action.payload.loginToken); // TS is happy!
        break;

    default:
        console.log('NOT A SUCCESS ACTION');
}

Here's a quick repro in TS playground