Reactjs 带参数的类型安全Redux Reducer

Reactjs 带参数的类型安全Redux Reducer,reactjs,typescript,redux,Reactjs,Typescript,Redux,我正在尝试将typescript与react redux一起使用,我在reducer方面遇到了一些问题。在当前的设置中,我会出现错误,但我不明白为什么会出现这种情况。如果我偏离redux文档中的方法,我可以得到一些有用的东西,但它是重复的 ----代码:----- 我在三个不同的目录中有三个文件,类似于: store |_actions | |_auth.ts |_reducers | |_auth.ts |_types |_actionTypes.ts |_auth

我正在尝试将typescript与react redux一起使用,我在reducer方面遇到了一些问题。在当前的设置中,我会出现错误,但我不明白为什么会出现这种情况。如果我偏离redux文档中的方法,我可以得到一些有用的东西,但它是重复的

----代码:-----

我在三个不同的目录中有三个文件,类似于:

store
|_actions
|   |_auth.ts  
|_reducers
|   |_auth.ts
|_types
    |_actionTypes.ts
    |_auth.ts
操作/auth.ts
文件中:

// actions/auth.ts
import { AuthActionTypes } from '../types/auth';
import * as actionTypes from '../types/actionTypes';

export const authStart = (): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_START
    };
};

export const authSuccess = ( token: string, userId: string ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_SUCCESS,
        idToken: token,
        userId: userId
    };
};

export const authFail = ( error: Error ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_FAIL,
        error: error
    };
};
// reducers/auth.ts
import * as actionTypes from '../types/actionTypes';
import { AuthState, AuthActionTypes } from '../types/auth';
import { updateObject } from '../utility';

const initialState: AuthState = {
    token: null,
    userId: null,
    error: null,
    loading: false,
    authRedirectPath: '/'
};

const authStart = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {error: null, loading: true })
};

const authSuccess = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};

const authFail = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {
        error: action.error, // ##### this is giving me an error #####
        loading: false
    });
};

const reducer = (state = initialState, action: AuthActionTypes): AuthState => {
    switch (action.type) {
        case actionTypes.AUTH_START: return authStart( state, action );
        case actionTypes.AUTH_SUCCESS: return authSuccess( state, action );
        case actionTypes.AUTH_FAIL: return authFail( state, action );
        default: 
            return state;
    };
};

export default reducer;
// types/auth.ts
import * as actionTypes from './actionTypes';

export interface AuthState {
    token: null | string
    userId: null | string
    error: null | Error
    loading: boolean
    authRedirectPath: string
} 

interface AuthStart {
    type: typeof actionTypes.AUTH_START
}

interface AuthSuccess {
    type: typeof actionTypes.AUTH_SUCCESS;
    idToken: string;
    userId: string;
}

interface AuthFail {
    type: typeof actionTypes.AUTH_FAIL;
    error: Error;
}

export type AuthActionTypes = 
| AuthStart 
| AuthSuccess
| AuthFail;
reducers/auth.ts
文件:

// actions/auth.ts
import { AuthActionTypes } from '../types/auth';
import * as actionTypes from '../types/actionTypes';

export const authStart = (): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_START
    };
};

export const authSuccess = ( token: string, userId: string ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_SUCCESS,
        idToken: token,
        userId: userId
    };
};

export const authFail = ( error: Error ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_FAIL,
        error: error
    };
};
// reducers/auth.ts
import * as actionTypes from '../types/actionTypes';
import { AuthState, AuthActionTypes } from '../types/auth';
import { updateObject } from '../utility';

const initialState: AuthState = {
    token: null,
    userId: null,
    error: null,
    loading: false,
    authRedirectPath: '/'
};

const authStart = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {error: null, loading: true })
};

const authSuccess = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};

const authFail = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {
        error: action.error, // ##### this is giving me an error #####
        loading: false
    });
};

const reducer = (state = initialState, action: AuthActionTypes): AuthState => {
    switch (action.type) {
        case actionTypes.AUTH_START: return authStart( state, action );
        case actionTypes.AUTH_SUCCESS: return authSuccess( state, action );
        case actionTypes.AUTH_FAIL: return authFail( state, action );
        default: 
            return state;
    };
};

export default reducer;
// types/auth.ts
import * as actionTypes from './actionTypes';

export interface AuthState {
    token: null | string
    userId: null | string
    error: null | Error
    loading: boolean
    authRedirectPath: string
} 

interface AuthStart {
    type: typeof actionTypes.AUTH_START
}

interface AuthSuccess {
    type: typeof actionTypes.AUTH_SUCCESS;
    idToken: string;
    userId: string;
}

interface AuthFail {
    type: typeof actionTypes.AUTH_FAIL;
    error: Error;
}

export type AuthActionTypes = 
| AuthStart 
| AuthSuccess
| AuthFail;
类型/auth.ts
文件:

// actions/auth.ts
import { AuthActionTypes } from '../types/auth';
import * as actionTypes from '../types/actionTypes';

export const authStart = (): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_START
    };
};

export const authSuccess = ( token: string, userId: string ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_SUCCESS,
        idToken: token,
        userId: userId
    };
};

export const authFail = ( error: Error ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_FAIL,
        error: error
    };
};
// reducers/auth.ts
import * as actionTypes from '../types/actionTypes';
import { AuthState, AuthActionTypes } from '../types/auth';
import { updateObject } from '../utility';

const initialState: AuthState = {
    token: null,
    userId: null,
    error: null,
    loading: false,
    authRedirectPath: '/'
};

const authStart = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {error: null, loading: true })
};

const authSuccess = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};

const authFail = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {
        error: action.error, // ##### this is giving me an error #####
        loading: false
    });
};

const reducer = (state = initialState, action: AuthActionTypes): AuthState => {
    switch (action.type) {
        case actionTypes.AUTH_START: return authStart( state, action );
        case actionTypes.AUTH_SUCCESS: return authSuccess( state, action );
        case actionTypes.AUTH_FAIL: return authFail( state, action );
        default: 
            return state;
    };
};

export default reducer;
// types/auth.ts
import * as actionTypes from './actionTypes';

export interface AuthState {
    token: null | string
    userId: null | string
    error: null | Error
    loading: boolean
    authRedirectPath: string
} 

interface AuthStart {
    type: typeof actionTypes.AUTH_START
}

interface AuthSuccess {
    type: typeof actionTypes.AUTH_SUCCESS;
    idToken: string;
    userId: string;
}

interface AuthFail {
    type: typeof actionTypes.AUTH_FAIL;
    error: Error;
}

export type AuthActionTypes = 
| AuthStart 
| AuthSuccess
| AuthFail;
为完整起见,
reducers/auth.ts中使用的
utility.ts
文件中的
updateObject
函数

export const updateObject = <T, U>(oldObject: T, updatedProperties: U): T => {
    return {
        ...oldObject,
        ...updatedProperties
    };
};
我们可以看到
idToken
userId
都是在
界面中定义的。我不明白为什么它会引用
AuthStart
,而不是
authsucture
。如果我导出
AuthSucess
接口并将其声明为该函数的操作类型,则错误消息将消失。是否有任何方法将
操作保持为
AuthActionType
,或者是否需要显式声明为
AuthSuces
,并且对于使用此设置的其他操作也是如此

另外,如果我删除了所有的箭头函数,并在错误不再存在的情况下直接定义了逻辑。如果我有

// reducers/auth.ts
...
const reducer = (state = initialState, action: AuthActionTypes): AuthState => {
    switch (action.type) {
        ...
        case actionTypes.AUTH_SUCCESS: 
            return { 
               ...state, 
               token: action.idToken, 
               userId: action.userId, 
               error: null,
             };
...

由于我的解决方案更易于维护,因此有没有任何方法可以保持我现在的设置而不必直接在swtich案例中使用上述逻辑?

考虑一下
AuthSuccess
函数本身的定义:

const authSuccess = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};
此定义表明,
action
可以是任何
authactionType
,包括缺少
idToken
属性的
AuthStart
。而是将函数键入为

const authSuccess = ( state: AuthState, action: AuthSuccess ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};
reducer中的
开关
语句将允许typescript将传递给
authSuccess
操作
的类型细化为仅
authSuccess
而不是其他
AuthActionTypes


这就是内联这些函数工作正常的原因-编译器知道在
情况下
authsucture
块中的操作必须是
authsucture
类型,因此
idToken
userId
是有效的。

考虑
authsucture
函数本身的定义:

const authSuccess = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};
此定义表明,
action
可以是任何
authactionType
,包括缺少
idToken
属性的
AuthStart
。而是将函数键入为

const authSuccess = ( state: AuthState, action: AuthSuccess ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};
reducer中的
开关
语句将允许typescript将传递给
authSuccess
操作
的类型细化为仅
authSuccess
而不是其他
AuthActionTypes


这就是为什么内联这些函数可以很好地工作的原因-编译器知道在
案例中的操作必须是
authsucture
类型,因此
idToken
userId
是有效的。

除了接受的答案之外,我们特别建议您切换到使用。它包括一些实用程序来简化几个常见的Redux用例,包括存储设置、定义缩减器、不可变的更新逻辑,甚至一次创建整个状态“切片”。它也已经用TypeScript编写,并且将最小化您必须编写的类型声明的数量


看看您的示例,使用
createSlice
可能会删除您已有的一半代码,并将其缩减为一个文件。

除了公认的答案之外,我们特别建议您切换到使用。它包括一些实用程序来简化几个常见的Redux用例,包括存储设置、定义缩减器、不可变的更新逻辑,甚至一次创建整个状态“切片”。它也已经用TypeScript编写,并且将最小化您必须编写的类型声明的数量


看看您的示例,使用
createSlice
可能会删除一半的代码,并将其缩减为一个文件。

清楚地告诉我们,您没有像idToken这样的属性接口清楚地告诉我们,您没有像idToken这样的属性接口