Ошибка машинописного текста TS2554: Ожидалось 0 аргументов, но получено 1. при использовании useReducer и useContext с React

#javascript #reactjs #typescript #use-context #use-reducer

Вопрос:

У меня есть несколько обучающих программ и useContext, в которых я получаю ошибки, аналогичные приведенным ниже (TS2554). Я выбрал authReducer, так как он самый простой. Я получаю одну и ту же ошибку при каждой отправке действия. Я поиграл, рассматривая другие решения и пробуя их, Добавил еще несколько определений, но ясно, что я делаю не то, что должен делать!

Это мой первый проект, над которым я работаю с машинописным текстом, так что полегче со мной!

насколько я могу судить, Диспетчерская не ожидает никаких аргументов, но получает их. Однако, когда я объявляю свой authReducer, он имеет аргумент действия типа Действия и возвращает состояние аутентификации.

 /AuthState.tsx:40:16 - error TS2554: Expected 0 arguments, but got 1.

40       dispatch({
41         type: USER_LOADED,
42         payload: res.data,
43       });
 

authReducer.tsx

 import {
  USER_LOADED,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT,
  CLEAR_ERRORS,
} from '../types';
import { initialState } from './AuthState';

type Actions =
  | {
      type: 'USER_LOADED';
      payload: {
        name: string;
        id: string;
      };
    }
  | {
      type: 'AUTH_ERROR';
      payload?: string;
    }
  | {
      type: 'LOGIN_SUCCESS';
      payload: { token: string };
    }
  | {
      type: 'LOGIN_FAIL';
      payload: string;
    }
  | {
      type: 'LOGOUT';
      payload?: string;
    }
  | {
      type: 'CLEAR_ERRORS';
    };

interface AuthState {
  token?: string | null;
  isAuthenticated?: boolean;
  loading: boolean;
  user?: {
    name: string;
    id: string;
  } | null;
  error?: string | undefined | null;
}

const AuthReducer = (
  state: typeof initialState,
  action: Actions
): AuthState => {
  switch (action.type) {
    case USER_LOADED:
      return {
        ...state,
        isAuthenticated: true,
        loading: false,
        user: action.payload,
      };
    case LOGIN_SUCCESS:
      localStorage.setItem('token', action.payload.token);
      return {
        ...state,
        ...action.payload,
        isAuthenticated: true,
        loading: false,
      };
    case AUTH_ERROR:
    case LOGIN_FAIL:
    case LOGOUT:
      localStorage.removeItem('token');
      return {
        ...state,
        token: null,
        isAuthenticated: false,
        loading: false,
        user: null,
        error: action.payload,
      };
    case CLEAR_ERRORS:
      return {
        ...state,
        error: null,
      };
    default:
      return state;
  }
};

export default AuthReducer;

 

AuthContext.tsx

 import { createContext } from 'react';
import { initialState } from './AuthState';

type authContextType = {
  loadUser: () => Promise<void> | null;
  login: (formData: {
    email: string;
    password: string;
  }) => Promise<void> | null;
  logout: () => void;
  clearErrors: () => void;

  error: string | null;
  isAuthenticated: boolean;
  loading: boolean;
  user: {
    name: string;
    id: string;
  };
  token: string;
};

const authContext = createContext<authContextType>(initialState); //TODO A more robust type is possible

export default authContext;

 

AuthState.tsx

 import React, { useReducer } from 'react';
import axios from 'axios';
import AuthContext from './AuthContext';
import AuthReducer from './AuthReducer';
import setAuthToken from '../../utils/setAuthToken';
import {
  USER_LOADED,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT,
  CLEAR_ERRORS,
} from '../types';

export const initialState = {
  loadUser: null,
  login: null,
  logout: () => {},
  clearErrors: () => {},

  token: localStorage.getItem('token'),
  isAuthenticated: null,
  loading: true,
  error: null,
  user: null,
};

const AuthState: React.FC = ({ children }) => {
  const [state, dispatch] = useReducer(AuthReducer, initialState);

  //Load User
  const loadUser = async () => {
    if (localStorage.token) {
      setAuthToken(localStorage.token);
    }

    try {
      const res = await axios.get('api/auth');

      dispatch({
        type: USER_LOADED,
        payload: res.data,
      });
    } catch (err) {
      dispatch({ type: AUTH_ERROR });
    }
  };
  //Login User
  const login = async (formData: { email: string; password: string }) => {
    const config = {
      headers: {
        'Content-Type': 'application/json',
      },
    };
    try {
      const res = await axios.post('/api/auth', formData, config);

      dispatch({
        type: LOGIN_SUCCESS,
        payload: res.data,
      });

      loadUser();
    } catch (err) {
      dispatch({
        type: LOGIN_FAIL,
        payload: err.response.data.msg,
      });
    }
  };
  //Logout
  const logout = () => {
    dispatch({ type: LOGOUT });
  };
  //Clear Errors
  const clearErrors = () => {
    dispatch({ type: CLEAR_ERRORS });
  };

  return (
    <AuthContext.Provider
      value={{
        token: state.token,
        isAuthenticated: state.isAuthenticated,
        loading: state.loading,
        user: state.user,
        error: state.error,
        loadUser,
        login,
        logout,
        clearErrors,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export default AuthState;

 

Комментарии:

1. state: typeof initialState, хотя эта линия выглядит неправильно, не так ли state: typeof AuthState, ?

2. Эй, Том, спасибо за ответ. Я попробовал и поиграл еще немного, но, к сожалению, это, похоже, не повлияло на данную ошибку

3. Я больше не вижу ничего странного -> не могли бы вы сказать мне, что > USER_LOADED находится в ../types папке

Ответ №1:

Проблема в том, что здесь:

 const AuthReducer = (
  state: typeof initialState,
  action: Actions
): AuthState ...
 

typescript не может связать ваши типы typeof initialState и AuthState . Но useReducer перегрузки зависят от типа reducer вывода правильного типа результата.

Здесь у вас есть несколько вариантов рефакторинга. Вы можете просто сказать typescript, что это одни и те же типы:

 const AuthReducer = (
  state: AuthState,
  action: Actions
): AuthState
 

или сделайте свой initialState тип немного более расслабленным:

 const initialState: AuthState = {....}
 

Суть проблемы заключается в том, что типы TypeofA и A :

 const a = { a: null }
type TypeofA = typeof a

type A = { a: string | null }
 

это не совсем одно и то же. Вы теряете | string при объявлении поля просто как null . Чтобы сделать их равными, вы можете либо написать a объект с утверждениями типа:

 const a = { a: null as string | null }
 

или прямо указать тип a :

 const a: A = { a: null }