在写项目的时候,Calculator遇到了这样的问题,给我的任务是把项目分成模块,如果需要的话,可以用其他的模块来替换,我们以API为例,API代码应该写得直观明了,怎么用其他的模块来替换。
因此,我决定键入负责API请求的函数。也许我做错了,如果你指出错误就太好了。
//./api/types.ts
export type HistoryItem = { //This is an interface of item for request, but not respond
expression: string;
result: string;
};
export type CalculationRequest<T> = (expression: string) => T[];
export type DeleteRequest = (id: string) => void;
export type GetAllRequest<T> = () => T[];
创建泛型的想法是API可以接受某种类型的数据作为输入并返回某种类型的数据,但在将该数据传递给状态之前,它必须是强类型的,以匹配状态的类型。
接下来,我编写了一个useApi
钩子,它只检查.env
,选择所需的API,并将fetch函数传递给切片。
import { defaultApi } from "@/api";
import {
CalculationRequest,
DeleteRequest,
GetAllRequest,
HistoryItem,
} from "@/api/types";
export type ApiMethods = {
calculateExpression: CalculationRequest<HistoryItem>;
deleteHistoryItem: DeleteRequest;
fetchHistory: GetAllRequest<HistoryItem>;
};
type UseApiHook = () => ApiMethods;
export const useApi: UseApiHook = () => {
if (process.env.NEXT_PUBLIC_REACT_APP_API === "default") {
return defaultApi;
} else {
throw new Error("API was not found!");
}
};
在切片中,我将获取函数 Package 在createAsyncThunk中并编写它们的功能。
const fetchHistory = createAsyncThunk(
"history/get",
api.fetchHistory
);
const deleteHistoryItem = createAsyncThunk(
"history/delete",
api.deleteHistoryItem
);
const calculateExpression = createAsyncThunk(
"calculator/get",
api.calculateExpression
);
const maxNumberOfHistoryItems = 10;
const initialState: CalculatorHistoryState = {
history: [],
inputValue: "0",
fetchError: null,
status: "idle",
};
const calculatorHistorySlice = createSlice({
name: "history",
initialState,
reducers: {
addItemToState(state, action) {
const updatedHistory = [action.payload, ...state.history];
if (updatedHistory.length < maxNumberOfHistoryItems) {
return { ...state, history: updatedHistory };
}
return {
...state,
history: updatedHistory.slice(0, maxNumberOfHistoryItems),
};
},
removeFromState(state, action) {
const filteredHistory = state.history.filter((item) => {
return item._id != action.payload;
});
return { ...state, history: filteredHistory };
},
setItemToInput(state, action) {
return { ...state, inputValue: action.payload };
},
},
extraReducers(builder) {
builder.addCase(HYDRATE, (state, _action) => {
return {
...state,
// ...action.payload.subject,
};
});
builder.addCase(fetchHistory.fulfilled, (state, { payload }) => {
state.history = [...payload];
state.status = "idle";
});
builder.addCase(calculateExpression.fulfilled, (state, _action) => {
return state;
});
builder.addCase(deleteHistoryItem.fulfilled, (state, action) => {
state.history.filter((item) => item._id != action.payload);
});
builder.addCase(deleteHistoryItem.rejected, (state, action) => {
console.log(action);
return state;
});
},
});
问题是我想把来自createAsyncThunk请求的数据进行类型化,比如更换API时,返回的数据类型可能会发生变化,但在进入状态之前,必须按照国家标准对数据进行格式化,这样以后才不会出错,直观上也容易理解。
export interface StateItem {
expression: string;
result: string;
_id: string;
__v?: string;
}
在代码中的什么地方应该为从API返回的状态指定数据类型?
1条答案
按热度按时间igsr9ssn1#
这里的基本问题似乎是在哪里使用
HistoryItem
类型,而在哪里使用StateItem
类型,StateItem
类型通过添加_id
来扩展HistoryItem
。您可以通过查看如何使用变量以及出现哪些TypeScript错误来判断哪种类型是合适的。
我复制并粘贴了您的代码into the TypeScript playground,并创建了一个类型为
ApiMethods
的伪api
变量。我看到的重要错误如下:类型“HistoryItem[]"不能分配给类型”WritableDraft[]“。
类型“HistoryItem”中缺少属性“_id”,但类型“WritableDraft”中需要该属性。
您的Reducer在许多地方使用了
_id
,因此我们知道我们需要state.history
为StateItem[]
而不是HistoryItem[]
。此错误告诉我们,我们还需要您的fetchHistory
thunk才能使用StateItem[]
类型。您的代码依赖于fetchHistory
API调用返回具有_id
属性的项数组的预期(如果这是一个不正确的假设,那么你需要做其他的改变)。您需要更改
ApiMethods
的类型。可能如下所示:但可能是这样的,如果你想说一个泛型API总是添加一个
_id
:你的
deleteHistoryItem.fulfilled
大小写还原器也有问题,因为你的action
没有payload
,如果DeleteRequest
返回的void
是准确的,你需要从参数中获取被删除项的id,而不是payload
。