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

Reactjs 如何在Typescript中为React useReducer钩子操作创建类型定义?,reactjs,typescript,types,react-hooks,Reactjs,Typescript,Types,React Hooks,假设我们有如下定义的userReducer: 函数userReducer(状态:string,操作:UserAction):string{ 开关(动作类型){ 案例“登录”: 返回action.username; 案例“注销”: 返回“”; 违约: 抛出新错误(“未知的“用户”操作”); } } 定义UserAction类型的最佳方法是什么,这样就可以使用username有效负载和不使用以下内容调用dispatch: dispatch({type:“LOGIN”,username:“Joe”}

假设我们有如下定义的
userReducer

函数userReducer(状态:string,操作:UserAction):string{
开关(动作类型){
案例“登录”:
返回action.username;
案例“注销”:
返回“”;
违约:
抛出新错误(“未知的“用户”操作”);
}
}
定义
UserAction
类型的最佳方法是什么,这样就可以使用
username
有效负载和不使用以下内容调用
dispatch

dispatch({type:“LOGIN”,username:“Joe”}});
/* ... */
分派({type:“LOGOUT”});
如果类型定义如下:

键入UserActionWithPayload={
类型:字符串;
用户名:字符串;
};
键入UserActionWithoutPayload={
类型:字符串;
};
导出类型UserAction=UserActionWithPayload | UserActionWithoutPayload;
编译器在“LOGIN”情况下在reducer中引发错误:
TS2339:类型“UserAction”上不存在属性“username”。“UserActionWithoutPayload”类型上不存在属性“username”。

如果使用可选成员定义了类型:

导出类型UserAction={
类型:字符串;
用户名?:字符串;
}
然后编译器显示此错误:
TS2322:类型“string | undefined”不可分配给类型“string”。类型“未定义”不能分配给类型“字符串”。

这里少了什么?也许整个方法都错了

项目使用TypeScript 3.8.3和React.js 16.13.0

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(…)函数返回类型和实数返回类型(作为字符串且未定义)之间有所不同。 要解决此问题,您可以将返回类型更改为任意类型

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

问候。
林。

这种方法看起来不错,问题是你的reducer的返回类型是
string
,但是如果它被传递了
UserActionWithoutPayload
,那么它可能返回
action.username
,其中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");
  }
}

经过数小时的挖掘和实验,通过Typescript
enum
和操作的联合类型,找到了一个非常优雅的解决方案:

enum UserActionType{
LOGIN=“LOGIN”,
LOGOUT=“注销”
}
类型UserState=string;
类型UserAction=
|{type:UserActionType.LOGIN;用户名:string}
|{type:UserActionType.LOGOUT}
函数userReducer(state:UserState,action:UserAction):字符串{
开关(动作类型){
案例UserActionType.LOGIN:
返回action.username;
案例UserActionType.LOGOUT:
返回“”;
违约:
抛出新错误();
}
}
函数App(){
const[user,userDispatch]=useReducer(userReducer,“”);
函数handleLogin(){
userDispatch({type:UserActionType.LOGIN,用户名:“Joe”});
}
函数handleLogout(){
userDispatch({type:UserActionType.LOGOUT});
}
/* ... */
}

使用上述方法不会出现错误或警告,而且操作使用有一个相当严格的合同。

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

键入UserActionWithPayload={
类型:字符串;
用户名:字符串;
};
键入UserActionWithoutPayload={
类型:字符串;
};
输入UserAction=UserActionWithPayload | UserActionWithoutPayload;
函数userReducer(状态:string,操作:UserAction):string{
开关(动作类型){
案例“登录”:
返回(作为UserActionWithPayload的操作)。用户名;
案例“注销”:
返回“”;
违约:
抛出新错误(“未知的“用户”操作”);
}
}
让国家=”;
state=userReducer(state,{type:“LOGIN”,username:“John”});
日志(“登录”,状态);
state=userReducer(state,{type:“LOGOUT”});
日志(“注销”,状态);

请访问此URL进行代码测试。不幸的是,这并没有帮助,因为减速机的实际使用,即:
const[user,userDispatch]=useReducer(userReducer,”)
给出错误:
没有重载与此调用匹配。重载1/5’(减速器:减速器无动作,初始化器arg:any,初始化器?:未定义):[any,DispatchWithoutAction]”,出现以下错误。类型为“(state:string,action:UserAction)=>string | undefined”的参数不能分配给类型为“ReducerWithoutAction”的参数。