使用带有 typescript 和 thunk 的 createStore 时出错
Error using createStore with typescript and thunk
我是 typescript 的新手,正在尝试将 React 应用程序转换为使用 typescript。
在我的 index.tsx 文件中使用 'redux' 的 createStore 函数时,出现以下错误。
Type '{}' is missing the following properties from type '{ customersLoading: boolean; customersLoadError: boolean; customers: {}; editCustomerInProgress: boolean; editCustomerSuccess: boolean; editCustomerFail: boolean; addCustomerInProgress: boolean; ... 6 more ...; searchResults: {}; }': customersLoading, customersLoadError, customers, editCustomerInProgress, and 10 more. TS2345
似乎状态被定义为一种{},但我已经为状态对象定义了一个接口。
Index.js
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import reducer from './store/reducer';
import * as serviceWorker from './serviceWorker';
const composeEnhancers = compose;
const rootReducer = reducer;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));
const app = (
<Provider store={store}>
<App />
</Provider>
);
ReactDOM.render(app, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
Reducer.tsx
import {stateShape, actionShape} from '../types/index';
const initialState = {
customersLoading: false,
customersLoadError: false,
customers: {},
editCustomerInProgress: false,
editCustomerSuccess: false,
editCustomerFail: false,
addCustomerInProgress: false,
addCustomerSuccess: false,
addCustomerFail: false,
deleteCustomerInProgress: false,
deleteCustomerSuccess: false,
deleteCustomerFail: false,
searchMode: false,
searchResults: {}
};
const updateObject = (oldObject: {}, updatedProperties: {}) => {
return {
...oldObject,
...updatedProperties
};
};
const editCustomer = (state: stateShape, action: actionShape) => {
let updatedState = {
editCustomerInProgress: false,
editCustomerFail: false,
editCustomerSuccess: true,
customers: {
...state.customers,
[action.payload.customerId]: action.payload.customerDetails
},
searchResults: {...state.searchResults}
};
if (state.searchMode){
updatedState = {
...updatedState,
searchResults: {
...state.searchResults,
[action.payload.customerId]: action.payload.customerDetails
}
}
};
return updateObject(state, updatedState);
};
const addCustomer = (state: stateShape, action: actionShape) => {
const updatedState = {
addCustomerInProgress: false,
addCustomerFail: false,
addCustomerSuccess: true,
customers: {
...state.customers,
[action.payload.customerId]: action.payload.customerData
}
};
return updateObject(state, updatedState);
};
const deleteCustomer = (state: stateShape, action: actionShape) => {
const updatedState = {
deleteCustomerInProgress: false,
deleteCustomerFail: false,
deleteCustomerSuccess: true,
customers: {
...state.customers,
[action.payload.customerId]: null
}
};
return updateObject(state, updatedState);
};
const searchCustomers = (state: stateShape, action: actionShape) => {
interface arrayInterface {
[key: number]: any
};
//filter matching customers from list of customers by search term
const matchingCustomers = Object.entries(state.customers).filter((current: arrayInterface) => {
let firstname = current[1].firstName.toLowerCase();
let lastname = current[1].lastName.toLowerCase();
let searchTerm = action.payload.searchTerm.toLowerCase();
return firstname === searchTerm || lastname === searchTerm;
});
let matchingCustomersObject;
matchingCustomers.forEach((current: arrayInterface) => {
matchingCustomersObject[current[0]] = current[1];
});
const updatedState = {
searchMode: true,
searchResults: matchingCustomersObject
};
return updateObject(state, updatedState);
};
let reducer = (state = initialState, action: actionShape) => {
switch (action.type){
case actionTypes.LOAD_CUSTOMERS_START:
return updateObject(state, {customersLoading: true});
case actionTypes.LOAD_CUSTOMERS_FAIL:
return updateObject(state, {customersLoading: false, customersLoadError: true});
case actionTypes.LOAD_CUSTOMERS_SUCCESS:
return updateObject(state, {customersLoading: false, customers: action.payload.customers});
case actionTypes.EDIT_CUSTOMER_START:
return updateObject(state, {editCustomerInProgress: true, editCustomerSuccess: false});
case actionTypes.EDIT_CUSTOMER_FAIL:
return updateObject(state, {editCustomerInProgress: false, editCustomerFail: true});
case actionTypes.EDIT_CUSTOMER_SUCCESS:
return editCustomer(state, action)
case actionTypes.ADD_CUSTOMER_START:
return updateObject(state, {addCustomerInProgress: true});
case actionTypes.ADD_CUSTOMER_FAIL:
return updateObject(state, {addCustomerInProgress: false, addCustomerFail: true});
case actionTypes.ADD_CUSTOMER_SUCCESS:
return addCustomer(state, action);
case actionTypes.DELETE_CUSTOMER_START:
return updateObject(state, {deleteCustomerInProgress: true});
case actionTypes.DELETE_CUSTOMER_FAIL:
return updateObject(state, {deleteCustomerInProgress: false, deleteCustomerFail: true});
case actionTypes.DELETE_CUSTOMER_SUCCESS:
return deleteCustomer(state, action);
case actionTypes.SEARCH_CUSTOMER_START:
return searchCustomers(state, action)
case actionTypes.SEARCH_CUSTOMER_END:
return updateObject(state, {searchMode: false, searchResults: []})
default:
return state;
}
};
export default reducer;
reducer.tsx
导入的动作和状态形状
customersLoading: boolean,
customersLoadError: boolean,
customers: {},
editCustomerInProgress: boolean,
editCustomerSuccess: boolean,
editCustomerFail: boolean,
addCustomerInProgress: boolean,
addCustomerSuccess: boolean,
addCustomerFail: boolean,
deleteCustomerInProgress: boolean,
deleteCustomerSuccess: boolean,
deleteCustomerFail: boolean,
searchMode: boolean,
searchResults: {}
};
export interface actionShape {
type: string,
payload: {
customerId: string,
customerData: Object,
customerDetails: Object,
customers: Object,
searchTerm: string
}
};
有没有人能指出正确的方向来解决这个问题?
问题来自 reducer
签名不兼容。让我们看看createStore
是如何定义的。
export interface StoreCreator {
<S, A extends Action, Ext, StateExt>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S & StateExt, A> & Ext
<S, A extends Action, Ext, StateExt>(
reducer: Reducer<S, A>,
preloadedState?: DeepPartial<S>,
enhancer?: StoreEnhancer<Ext>
): Store<S & StateExt, A> & Ext
}
本质上它是重载函数,我们对第一个参数的类型感兴趣,reducer
。
export type Reducer<S = any, A extends Action = AnyAction> = (
state: S | undefined,
action: A
) => S
Reducer
接受两个类型为 S
的参数(对于 state
)和类型为 A
(对于操作)和类型为 return 的值S
。
但是你的reducer
定义如下
let reducer = (state = initialState, action: actionShape) => { /*...*/ }
对于所有操作,它 returns updateObject(state, { /*...*/ })
又定义为
const updateObject = (oldObject: {}, updatedProperties: {}) =>
{
return {
...oldObject,
...updatedProperties
}
}
因此 TS 推断 updateObject
的 return 类型为 {}
并且它与 S
不兼容,在您的情况下为 stateShape
。
为了解决这个问题,我建议使用以下签名 updateObject
const updateObject = (oldObject: stateShape, updatedProperties: Partial<stateShape>): stateShape => {/*...*/}
所以它接受类型为 stateShape
的第一个参数,第二个参数为 Partial<stateShape>
。这允许您传递仅包含原始 stateShape
的某些属性的第二个参数,但禁止向状态添加不存在的属性。
更新 reducer
函数的签名以使其仅 return stateShape
也是个好主意
let reducer = (state = initialState, action: actionShape): stateShape
我是 typescript 的新手,正在尝试将 React 应用程序转换为使用 typescript。 在我的 index.tsx 文件中使用 'redux' 的 createStore 函数时,出现以下错误。
Type '{}' is missing the following properties from type '{ customersLoading: boolean; customersLoadError: boolean; customers: {}; editCustomerInProgress: boolean; editCustomerSuccess: boolean; editCustomerFail: boolean; addCustomerInProgress: boolean; ... 6 more ...; searchResults: {}; }': customersLoading, customersLoadError, customers, editCustomerInProgress, and 10 more. TS2345
似乎状态被定义为一种{},但我已经为状态对象定义了一个接口。
Index.js
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import reducer from './store/reducer';
import * as serviceWorker from './serviceWorker';
const composeEnhancers = compose;
const rootReducer = reducer;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));
const app = (
<Provider store={store}>
<App />
</Provider>
);
ReactDOM.render(app, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
Reducer.tsx
import {stateShape, actionShape} from '../types/index';
const initialState = {
customersLoading: false,
customersLoadError: false,
customers: {},
editCustomerInProgress: false,
editCustomerSuccess: false,
editCustomerFail: false,
addCustomerInProgress: false,
addCustomerSuccess: false,
addCustomerFail: false,
deleteCustomerInProgress: false,
deleteCustomerSuccess: false,
deleteCustomerFail: false,
searchMode: false,
searchResults: {}
};
const updateObject = (oldObject: {}, updatedProperties: {}) => {
return {
...oldObject,
...updatedProperties
};
};
const editCustomer = (state: stateShape, action: actionShape) => {
let updatedState = {
editCustomerInProgress: false,
editCustomerFail: false,
editCustomerSuccess: true,
customers: {
...state.customers,
[action.payload.customerId]: action.payload.customerDetails
},
searchResults: {...state.searchResults}
};
if (state.searchMode){
updatedState = {
...updatedState,
searchResults: {
...state.searchResults,
[action.payload.customerId]: action.payload.customerDetails
}
}
};
return updateObject(state, updatedState);
};
const addCustomer = (state: stateShape, action: actionShape) => {
const updatedState = {
addCustomerInProgress: false,
addCustomerFail: false,
addCustomerSuccess: true,
customers: {
...state.customers,
[action.payload.customerId]: action.payload.customerData
}
};
return updateObject(state, updatedState);
};
const deleteCustomer = (state: stateShape, action: actionShape) => {
const updatedState = {
deleteCustomerInProgress: false,
deleteCustomerFail: false,
deleteCustomerSuccess: true,
customers: {
...state.customers,
[action.payload.customerId]: null
}
};
return updateObject(state, updatedState);
};
const searchCustomers = (state: stateShape, action: actionShape) => {
interface arrayInterface {
[key: number]: any
};
//filter matching customers from list of customers by search term
const matchingCustomers = Object.entries(state.customers).filter((current: arrayInterface) => {
let firstname = current[1].firstName.toLowerCase();
let lastname = current[1].lastName.toLowerCase();
let searchTerm = action.payload.searchTerm.toLowerCase();
return firstname === searchTerm || lastname === searchTerm;
});
let matchingCustomersObject;
matchingCustomers.forEach((current: arrayInterface) => {
matchingCustomersObject[current[0]] = current[1];
});
const updatedState = {
searchMode: true,
searchResults: matchingCustomersObject
};
return updateObject(state, updatedState);
};
let reducer = (state = initialState, action: actionShape) => {
switch (action.type){
case actionTypes.LOAD_CUSTOMERS_START:
return updateObject(state, {customersLoading: true});
case actionTypes.LOAD_CUSTOMERS_FAIL:
return updateObject(state, {customersLoading: false, customersLoadError: true});
case actionTypes.LOAD_CUSTOMERS_SUCCESS:
return updateObject(state, {customersLoading: false, customers: action.payload.customers});
case actionTypes.EDIT_CUSTOMER_START:
return updateObject(state, {editCustomerInProgress: true, editCustomerSuccess: false});
case actionTypes.EDIT_CUSTOMER_FAIL:
return updateObject(state, {editCustomerInProgress: false, editCustomerFail: true});
case actionTypes.EDIT_CUSTOMER_SUCCESS:
return editCustomer(state, action)
case actionTypes.ADD_CUSTOMER_START:
return updateObject(state, {addCustomerInProgress: true});
case actionTypes.ADD_CUSTOMER_FAIL:
return updateObject(state, {addCustomerInProgress: false, addCustomerFail: true});
case actionTypes.ADD_CUSTOMER_SUCCESS:
return addCustomer(state, action);
case actionTypes.DELETE_CUSTOMER_START:
return updateObject(state, {deleteCustomerInProgress: true});
case actionTypes.DELETE_CUSTOMER_FAIL:
return updateObject(state, {deleteCustomerInProgress: false, deleteCustomerFail: true});
case actionTypes.DELETE_CUSTOMER_SUCCESS:
return deleteCustomer(state, action);
case actionTypes.SEARCH_CUSTOMER_START:
return searchCustomers(state, action)
case actionTypes.SEARCH_CUSTOMER_END:
return updateObject(state, {searchMode: false, searchResults: []})
default:
return state;
}
};
export default reducer;
reducer.tsx
导入的动作和状态形状 customersLoading: boolean,
customersLoadError: boolean,
customers: {},
editCustomerInProgress: boolean,
editCustomerSuccess: boolean,
editCustomerFail: boolean,
addCustomerInProgress: boolean,
addCustomerSuccess: boolean,
addCustomerFail: boolean,
deleteCustomerInProgress: boolean,
deleteCustomerSuccess: boolean,
deleteCustomerFail: boolean,
searchMode: boolean,
searchResults: {}
};
export interface actionShape {
type: string,
payload: {
customerId: string,
customerData: Object,
customerDetails: Object,
customers: Object,
searchTerm: string
}
};
有没有人能指出正确的方向来解决这个问题?
问题来自 reducer
签名不兼容。让我们看看createStore
是如何定义的。
export interface StoreCreator {
<S, A extends Action, Ext, StateExt>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S & StateExt, A> & Ext
<S, A extends Action, Ext, StateExt>(
reducer: Reducer<S, A>,
preloadedState?: DeepPartial<S>,
enhancer?: StoreEnhancer<Ext>
): Store<S & StateExt, A> & Ext
}
本质上它是重载函数,我们对第一个参数的类型感兴趣,reducer
。
export type Reducer<S = any, A extends Action = AnyAction> = (
state: S | undefined,
action: A
) => S
Reducer
接受两个类型为 S
的参数(对于 state
)和类型为 A
(对于操作)和类型为 return 的值S
。
但是你的reducer
定义如下
let reducer = (state = initialState, action: actionShape) => { /*...*/ }
对于所有操作,它 returns updateObject(state, { /*...*/ })
又定义为
const updateObject = (oldObject: {}, updatedProperties: {}) =>
{
return {
...oldObject,
...updatedProperties
}
}
因此 TS 推断 updateObject
的 return 类型为 {}
并且它与 S
不兼容,在您的情况下为 stateShape
。
为了解决这个问题,我建议使用以下签名 updateObject
const updateObject = (oldObject: stateShape, updatedProperties: Partial<stateShape>): stateShape => {/*...*/}
所以它接受类型为 stateShape
的第一个参数,第二个参数为 Partial<stateShape>
。这允许您传递仅包含原始 stateShape
的某些属性的第二个参数,但禁止向状态添加不存在的属性。
更新 reducer
函数的签名以使其仅 return stateShape
let reducer = (state = initialState, action: actionShape): stateShape