javascript - Typescript 错误 TS2554:预期 0 个参数,但收到 1 个。在使用 React 的 useReducer 和 useContext 时出现
我有一些 useReducers 和 useContext,其中出现了类似下面列出的错误(TS2554)。我选出了 AuthReducer,因为它是最简单的。每次 Action dispatch 都会出现相同的错误。我尝试过查看其他解决方案并尝试它们,添加了一些更多的定义,但显然我没有按正确的方式去做!
这是我第一个使用 TypeScript 的项目,所以请手下留情!
据我所知,Dispatch 不期望任何参数,但却收到了一个参数。然而,当我声明我的 AuthReducer 时,它有一个类型为 Actions 的 action 参数,并返回 AuthState。
/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
问题在于这里:
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 }
并不完全相同。当仅将字段声明为 null 时,你会丢失 | string。要使它们相等,你可以编写带有类型断言的 a 对象:
const a = { a: null as string | null }
或者直接说明 a 的类型:
const a: A = { a: null }
