如何在Typescript中为React useReducer钩子操作创建类型定义



假设我们有这样定义的userReducer

function userReducer(state: string, action: UserAction): string {
switch (action.type) {
case "LOGIN":
return action.username;
case "LOGOUT":
return "";
default:
throw new Error("Unknown 'user' action");
}
}

定义UserAction类型的最佳方法是什么,这样就可以在有username有效载荷和没有的情况下调用dispatch

dispatch({ type: "LOGIN", username: "Joe"}});
/* ... */
dispatch({ type: "LOGOUT" });

如果类型定义如下:

type UserActionWithPayload = {
type: string;
username: string;
};
type UserActionWithoutPayload = {
type: string;
};
export type UserAction = UserActionWithPayload | UserActionWithoutPayload;

"LOGIN"情况下编译器抛出并在reducer中出错:TS2339: Property 'username' does not exist on type 'UserAction'.   Property 'username' does not exist on type 'UserActionWithoutPayload'.

如果类型是用可选成员定义的:

export type UserAction = {
type: string;
username?: string;
}

然后编译器显示此错误:TS2322: Type 'string | undefined' is not assignable to type 'string'.   Type 'undefined' is not assignable to type 'string'.

这里少了什么?也许整个方法是错误的?

项目使用TypeScript 3.8.3和React.js 16.13.0。

经过数小时的挖掘和实验,通过Typescriptenum和动作的并集类型找到了一个非常优雅的解决方案:

enum UserActionType {
LOGIN = "LOGIN",
LOGOUT = "LOGOUT"
}
type UserState = string;
type UserAction =
| { type: UserActionType.LOGIN; username: string }
| { type: UserActionType.LOGOUT }
function userReducer(state: UserState, action: UserAction): string {
switch (action.type) {
case UserActionType.LOGIN:
return action.username;
case UserActionType.LOGOUT:
return "";
default:
throw new Error();
}
}
function App() {
const [user, userDispatch] = useReducer(userReducer, "");
function handleLogin() {
userDispatch({ type: UserActionType.LOGIN, username: "Joe" });
}
function handleLogout() {
userDispatch({ type: UserActionType.LOGOUT });
}
/* ... */
}

使用上面的方法没有错误或警告,而且有一个非常严格的操作使用合同。

function userReducer(state: string, action: UserAction): any {
switch (action.type) {
case "LOGIN":
return action.username;
case "LOGOUT":
return "";
default:
throw new Error("Unknown 'user' action");
}
}

在这个函数中,您将返回类型定义为字符串。但是,当您将UserAction传递为UserActionWithoutPayload类型时,返回值应该是未定义的。这些原因在userReducer(…(函数返回类型和实际返回类型之间有所不同,前者是字符串,后者是未定义的。要解决此问题,可以将返回类型更改为任意类型。

我希望这个答案对你的工作有帮助。

谨致问候。Lin.

这种方法看起来不错,问题是reducer的返回类型为string,但如果它被传递了UserActionWithoutPayload,那么它可能会返回未定义用户名的action.username

因此,解决问题的一种方法是放松您的退货类型:

function userReducer(state: string, action: UserAction): string | undefined {
switch (action.type) {
case "LOGIN":
return action.username;
case "LOGOUT":
return "";
default:
throw new Error("Unknown 'user' action");
}
}

您需要通过类型转换动作来为特定的减速器案例提供更多的类型信息

type UserActionWithPayload = {
type: string;
username: string;
};
type UserActionWithoutPayload = {
type: string;
};
type UserAction = UserActionWithPayload | UserActionWithoutPayload;
function userReducer(state: string, action: UserAction): string {
switch (action.type) {
case "LOGIN":
return (action as UserActionWithPayload).username;
case "LOGOUT":
return "";
default:
throw new Error("Unknown 'user' action");
}
}
let state = "";
state = userReducer(state, { type: "LOGIN", username: "John" });
console.log("LOGIN", state);
state = userReducer(state, { type: "LOGOUT" });
console.log("LOGOUT", state);

游乐场链接

相关内容

  • 没有找到相关文章

最新更新