Свойство не существует при ошибке типа с объединением типов в Typescript Reac + Redux - PullRequest
0 голосов
/ 20 ноября 2018

Я новичок в React и Redux, и я делаю небольшой проект с использованием Typescript.Я создал свой первый редуктор и действия, но у меня есть проблема: всякий раз, когда я пытаюсь получить доступ к содержимому полезной нагрузки, я получаю Type error: property X does not exist in type.

Действия:

import { Action } from 'redux';
export const LOGIN_ACTION = 'login';
export const LOGIN_SUCCESS_ACTION = 'login-sucesss';
export const LOGIN_FAILED_ACTION = 'login-failed';

export interface LoginAction extends Action {
  type: string;
  payload: {
    username: string;
    password: string;
  };
}

export function login(username: string, password: string): LoginAction {
  return {
    type: LOGIN_ACTION,
    payload: { username, password },
  };
}

export interface LoginSuccessAction extends Action {
  type: string;
  payload: {
    loginToken: string;
  };
}

export function loginSuccess(loginToken: string): LoginSuccessAction {
  return {
    type: LOGIN_SUCCESS_ACTION,
    payload: { loginToken },
  };
}

export interface LoginFailedAction extends Action {
  type: string;
  payload: {
    error: Error;
  };
}

export function loginFailed(error: Error): LoginFailedAction {
  return {
    type: LOGIN_FAILED_ACTION,
    payload: { error },
  };
}

export type LoginActions = LoginAction | LoginSuccessAction | LoginFailedAction;

Редуктор:

import { LOGIN_ACTION, LOGIN_FAILED_ACTION, LOGIN_SUCCESS_ACTION, LoginActions } from '../../actions/login.action';
import {
  REQ_STATUS_FAIL,
  REQ_STATUS_PROCESSING,
  REQ_STATUS_SUCCESS,
  REQ_STATUS_UNDEFINED,
} from '../../common/request-status';

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

export const initialState: LoginState = {
  username: '',
  password: '',
  loginToken: '',
  loginError: undefined,
  status: REQ_STATUS_UNDEFINED,
};

export function loginReducer(state: LoginState = initialState, action: LoginActions): LoginState {
  switch (action.type) {
    case LOGIN_ACTION:
      console.log('I was here', action.payload);
      return { ...state, username: action.payload.username, password: action.payload.password, status: REQ_STATUS_PROCESSING };

    case LOGIN_SUCCESS_ACTION:
      return { ...state, loginToken: action.payload.loginToken, status: REQ_STATUS_SUCCESS };

    case LOGIN_FAILED_ACTION:
      return { ...state, loginToken: '', loginError: action.payload.error, status: REQ_STATUS_FAIL };

    default:
      return state;
  }
}

Проблема в том, что у меня много ошибок типа при доступе к свойствам полезной нагрузки, например, здесь: action.payload.username или action.payload.password.

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

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

Не могли бы вы мне помочь?

1 Ответ

0 голосов
/ 20 ноября 2018

Вы пытаетесь использовать различимые союзы в машинописи.Дискриминационные союзы работают с switch утверждениями, чтобы сузить тип в каждой ветви в соответствии с заданным свойством (type в вашем случае).Требование различаемых объединений состоит в том, чтобы свойство type было литеральным типом (в вашем случае строковым литеральным типом).Так как вы определяете константы для типа действия, вы можете использовать typeof constant для получения строкового литерала, выведенного для константы.

interface Action { } // Dummy for self contained sample
export const LOGIN_ACTION = 'login';
export const LOGIN_SUCCESS_ACTION = 'login-sucesss';
export const LOGIN_FAILED_ACTION = 'login-failed';

export interface LoginAction extends Action {
  type: typeof LOGIN_ACTION; // !! here we assign the string literal type of the constant
  payload: {
    username: string;
    password: string;
  };
}

export function login(username: string, password: string): LoginAction {
  return {
    type: LOGIN_ACTION,
    payload: { username, password },
  };
}

export interface LoginSuccessAction extends Action {
  type: typeof LOGIN_SUCCESS_ACTION; // !! here we assign the string literal type of the constant
  payload: {
    loginToken: string;
  };
}

export function loginSuccess(loginToken: string): LoginSuccessAction {
  return {
    type: LOGIN_SUCCESS_ACTION,
    payload: { loginToken },
  };
}

export interface LoginFailedAction extends Action {
  type: typeof LOGIN_FAILED_ACTION; // !! here we assign the string literal type of the constant
  payload: {
    error: Error;
  };
}

export function loginFailed(error: Error): LoginFailedAction {
  return {
    type: LOGIN_FAILED_ACTION,
    payload: { error },
  };
}

export type LoginActions = LoginAction | LoginSuccessAction | LoginFailedAction;


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

export const initialState: LoginState = {
  username: '',
  password: '',
  loginToken: '',
  loginError: undefined,
  status: 0,
};

export function loginReducer(state: LoginState = initialState, action: LoginActions): LoginState {
  // Type guard fro discriminated union.
  switch (action.type) {
    case LOGIN_ACTION:
      console.log('I was here', action.payload);
      // action is LoginAction here
      return { ...state, username: action.payload.username, password: action.payload.password, status: 0};

    case LOGIN_SUCCESS_ACTION:
      // action is LoginSuccessAction here
      return { ...state, loginToken: action.payload.loginToken, status: 1 };

    case LOGIN_FAILED_ACTION:
      // action is LoginFailedAction here 
      return { ...state, loginToken: '', loginError: action.payload.error, status: -1 };

    default:
      return state;
  }
}
...