简体   繁体   中英

Property does not exist on type error with Union Types in Typescript Reac + Redux

I am new to React and Redux and I am making a small project using Typescript. I created my first reducer and actions, but I have a problem: whenever I try to access the payload's contents I get a Type error: property X does not exist in type .

Actions:

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;

Reducer:

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

The problem is that I have a lot of type errors when accessing the payload's properties, such as here: action.payload.username or 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; }'.

Could you help me?

You are trying to use discriminated unions in typescript. Discriminated unions work with switch statements in order to narrow the type on each branch according to a given property ( type in your case). A requirement of discriminated unions is for the type property to be a literal type (in your case a string literal type). Since you define constants for the type of the action you can use typeof constant to get the string literal type inferred for the 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;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM