TypeScript错误::类型'{auth:IAuthState;错误:IErrorState; }'不能分配给类型'{auth:never;错误:从不; }'。ts(2322)

时间:2020-10-09 18:17:17

标签: reactjs typescript redux react-redux

我正在尝试使用React和Typescript设置我的第一个项目,但是我在rootReducer中遇到了这个错误:

Type 'Reducer<CombinedState<{ auth: never; error: never; }>, IRegisterUserAction | IAuthenticateUserAction | LogoutAction | ConfirmAccountAction | ISetErrorAction | IHideErrorAction>' is not assignable to type 'Reducer<{ auth: IAuthState; error: IErrorState; }, AnyAction>'.

参数“状态”和“状态”的类型不兼容。 输入'{auth:IAuthState;错误:IErrorState; } | undefined'不能分配给'CombinedState <{auth:never;错误:从不; }> |未定义”。 输入'{auth:IAuthState;错误:IErrorState; }'不能分配给'CombinedState <{auth:never;错误:从不; }>'。 输入'{auth:IAuthState;错误:IErrorState; }'不能分配给类型'{auth:never;错误:从不; }'。ts(2322)

我的rootReducer.ts:

const persistConfig = {
    key: 'root',
    storage,};

export const appReducers: Reducer<{ auth: IAuthState; error: IErrorState }> = combineReducers({
    auth: authReducer,
    error: errorReducer,});

export type RootState = ReturnType<typeof appReducers>;

const rootReducer = (state: CombinedState<RootState> | undefined, action: AuthActionTypes) => {
    if (action.type === LOGOUT) {
        state = undefined;
        localStorage.clear();
    }
    return appReducers(state, action);};

export const persistedReducer = persistReducer(persistConfig, rootReducer);

我的authReducer.ts:

export const initialState = {
    loggedIn: false,
    username: null,
    email: null,
    role: null,
    profileImage: null,};

const authReducer: Reducer<IAuthState, AuthActionTypes> = (state = initialState, action): IAuthState => {
switch (action.type) {
    case AUTHENTICATE_USER: {
        return {
            ...state,
            loggedIn: true,
            username: action.payload.username || null,
            email: action.payload.email || null,
            role: action.payload.role || null,
            profileImage: action.payload.profileImage || null,
        };
    }
    default:
        return state;
}};
export default authReducer;

我的errorReducer.ts:

export const initialState = {
    error: null,
    validationErrors: {
        username: null,
        email: null,
        password: null,
    },
};

const errorReducer: Reducer<IErrorState, ErrorActionTypes> = (state = initialState, action): IErrorState => {
  switch (action.type) {
    case SET_ERROR: {
        return {
            ...state,
            error: action.payload.error || null,
            validationErrors: {
                username: action.payload.validationErrors.username || null,
                email: action.payload.validationErrors.email || null,
                password: action.payload.validationErrors.password || null
            }
        };
    }
    default:
        return state;
}};
export default errorReducer;

我的store.ts:

export type AppState = {
    auth: IAuthState;
    error: IErrorState;
};
const initialState: AppState = {
    auth: authInitialState,
    error: errorInitialState
};

const middlewares = [thunk];

const configureStore = () => {
    const store = createStore(persistedReducer, initialState, 
         composeWithDevTools(applyMiddleware(...middlewares)));
    return store;
}

const store = configureStore();
const persistor = persistStore(store);

export { store, persistor };

我的authTypes.ts:

export const REGISTER_USER = 'REGISTER_USER';
export const AUTHENTICATE_USER = 'AUTHENTICATE_USER';
export const CONFIRM_ACCOUNT = 'CONFIRM_ACCOUNT';
export const LOGOUT = 'LOGOUT';

export type UserRegisterState = {
    username: string;
    email: string;
    password: string;
    passwordConfirm: string;
};

export type UserLoginState = {
    username: string;
    password: string;
};

export interface IRegisterUserAction extends Action<typeof REGISTER_USER> {
    type: typeof REGISTER_USER;
}

export interface IAuthenticateUserAction extends Action<typeof AUTHENTICATE_USER> {
    type: typeof AUTHENTICATE_USER;
    payload: IAuthState;
}

export interface LogoutAction extends Action<typeof LOGOUT> {
    type: typeof LOGOUT;
}

export interface ConfirmAccountAction extends Action<typeof CONFIRM_ACCOUNT> {
    type: typeof CONFIRM_ACCOUNT;
}

export type AuthActionTypes = IAuthenticateUserAction | LogoutAction | ConfirmAccountAction 
    | IRegisterUserAction;

我的errorTypes.ts:

export const SET_ERROR = 'SET_ERROR';
export const HIDE_ERROR = 'HIDE_ERROR';

export interface ISetErrorAction extends Action<typeof SET_ERROR> {
    type: typeof SET_ERROR;
    payload: IErrorState;
}

export interface IHideErrorAction extends Action<typeof HIDE_ERROR> {
    type: typeof HIDE_ERROR;
}

export type ErrorActionTypes = ISetErrorAction | IHideErrorAction;

我的authActions.ts:

export const registerUser: ActionCreator<ThunkAction<
    Promise<void>,
    RootState,
    UserRegisterState,
    IRegisterUserAction
>> = (userData: UserRegisterState) => async (dispatch: Dispatch) => {
    try {
        await signUpUser(userData);
    } catch (error) {
        dispatch({
            type: SET_ERROR,
            payload: {
                error: error.response.data.message,
                validationErrors: error.response.data.validationErrors,
            },
       });
    }
};

export const authenticateUser: ActionCreator<ThunkAction<
    Promise<void>,
    RootState,
    UserRegisterState,
    IAuthenticateUserAction
>> = (userData: UserLoginState) => async (dispatch: Dispatch) => {
    try {
        const user = await signInUser(userData);
        dispatch({
            type: AUTHENTICATE_USER,
            payload: user.data,
        });
    } catch (error) {
        dispatch({
            type: SET_ERROR,
            payload: {
                error: error.response.data.message,
                validationErrors: error.response.data.validationErrors,
            },
        });
    }
};

export const forgotUserPassword: ActionCreator<ThunkAction<
    Promise<void>,
    RootState,
    { userEmail: string },
    IAuthenticateUserAction
>> = (userEmail: string) => async (dispatch: Dispatch) => {
    try {
        await forgotPassword(userEmail);
    } catch (error) {
        dispatch({
            type: SET_ERROR,
            payload: {
                error: error.response.data.message,
                validationErrors: error.response.data.validationErrors,
            },
        });
    }
};

1 个答案:

答案 0 :(得分:0)

您的错误

const rootReducer = (state, action) => {
    if (action.type === LOGOUT) {
        state = undefined;
        localStorage.clear();
    }
    return appReducers(state, action);
};

在此函数的“注销”分支中,您将状态设置为undefined,这是不应该执行的,因为状态绝不能直接变异。减速器应返回状态的新副本。

起初我以为你在这里什么也没回来。现在,我知道您正在将状态设置为undefined,以便可以恢复到初始状态。可以用appReducers调用undefined,但是您不能为此改变状态。尝试这样的事情:

const rootReducer: RootReducer = (state, action) => {
    if (action.type === LOGOUT) {
        localStorage.clear();
        return appReducers(undefined, action);
    }
    return appReducers(state, action);
};

类型建议

您在type AppState中的store.tstype RootState中的rootReducer.ts都有相同的定义。因为值匹配,所以从技术上讲现在这不是问题,但是您希望将一个规范的定义放在一个位置。该位置应为rootReducer.ts文件,因为避免循环导入是一种很好的做法,因此,当商店已经从reducer导入时,我们不希望将reducer从商店中导入。

您的rootReducer在需要包含错误操作的情况下,将操作键入为AuthActionTypes。这可能是错误的来源,但是当前的错误是关于状态而不是操作。

您的类型应如下所示:

export interface RootState {
  auth: IAuthState;
  error: IErrorState;
};

export type RootAction = AuthActionTypes | ErrorActionTypes;

export type RootReducer = Reducer<RootState, RootAction>

export const appReducers: RootReducer = ....

const rootReducer: RootReducer = (state, action) => {...};

使用reducer函数,您可以在函数本身中添加RootReducer类型,也可以在参数和返回值中添加类型,但是不必同时执行。

注意:当我将鼠标悬停在类型为state的函数上的RootReducer上时,我看到state: RootState却没有提到它可能是undefined。我不知道为什么这是因为Reducer的定义很清楚,状态可以是指定的类型,也可以是undefinedtype Reducer<S = any, A extends Action<any> = AnyAction> = (state: S | undefined, action: A) => S