使用 Typescript 在 Redux-Saga 中键入生成器函数

Ale*_*Sil 9 typescript reactjs redux redux-saga

最近我开始重构我的 React 项目以开始在其中使用 Typescript。我在打字时遇到了一个问题,我不知道如何克服。

\n

技术堆栈:Typescript、React、Redux、Redux-Saga

\n

我还将 ESLint 与 AirBnB 规则集一起使用。

\n

我正在尝试编写一个使用授权服务中定义的异步函数的传奇。下面的代码是我第二次尝试编写此代码。我已经拆分了signIn saga,以便不按照本文末尾的建议混合yield返回类型。

\n
export function* callSignInService(userCredentials: UserCredentialsInterface): \n    Generator<CallEffect, UserAuthDataInterface, UserAuthDataInterface> > \n{\n    return yield call(signInService, userCredentials);\n}\n\nexport function* signIn(action: PayloadAction<UserCredentialsInterface>): \n    Generator<StrictEffect, void, PutEffect> \n{\n    try {\n        const { payload } = action;\n        const userData = yield* callSignInService(payload);\n\n        yield put(signInSuccess(userData));\n    } catch (error) {\n        yield put(signInFailure(error.message));\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

登录服务的简化版本:

\n
const signIn = async ( userCredentials: UserCredentialsInterface ): Promise<UserAuthDataInterface>\n{\n    /* ... Some credential format validations ... */\n    try {\n        const response = await axios.post(\'someUrl\', userCredentials);\n        return {\n            /* objectContent */\n        }: UserAuthDataInterface\n    } catch (error) {\n        throw new Error(AuthorizationErrorType.SERVER_ERROR);\n    }    \n}\n
Run Code Online (Sandbox Code Playgroud)\n

问题是我仍然收到错误:

\n
const userData = yield* callSignInService(payload);\n
Run Code Online (Sandbox Code Playgroud)\n

说:

\n
\n

TS2766:无法将迭代委托给值,因为其迭代器的“next”方法需要类型“UserAuthDataInterface”,但包含的生成器将始终发送“PutEffect”。\xc2\xa0\xc2\xa0Type \'SimpleEffect<"PUT", PutEffectDescriptor>\' 缺少类型 \'UserAuthDataInterface\' 中的以下属性:用户、令牌

\n
\n

我知道问题在于我的登录生成器NextType设置为PutEffect,并且它期望表达式yield signInService(userCredentials)返回PutEffect,但这不是我的目标。\n我想使用 signInService获取UserAuthData 然后将这些数据放入signInSuccess操作中(signInSuccess是使用操作创建器创建的)。

\n

我已经阅读了一些有关如何正确键入生成器的资源,但仍然无法使其与我的示例一起使用。我发现有关 redux-saga 在打字方面存在或曾经有问题的信息,但我不确定 2021 年是否仍然如此。

\n

我在这里错过了一些明显的事情吗?也许我尝试写这样的传奇有问题。如果有人可以看一下并给我如何处理这个问题的建议,我会很高兴。

\n

Lin*_*ste 12

yield第一个泛型类型参数是saga 中 ed 值的类型。换句话说,这就是“步骤”。

转移callSignInService到单独的函数实际上对类型没有帮助,因为我们仍在yield处理该结果。

您的signIn传奇有两个不同类型的步骤——调用 API 和分派操作。泛型需要是两种类型的联合。

export function* signIn(
  action: PayloadAction<UserCredentialsInterface>
): Generator<
  // step types
  CallEffect<UserAuthDataInterface> | PutEffect<AnyAction>,
  // return type
  void,
  // intermediate argument
  UserAuthDataInterface
> {
  try {
    const { payload } = action;
    const userData: UserAuthDataInterface = yield call(signInService, payload);

    yield put(signInSuccess(userData));
  } catch (error) {
    yield put(signInFailure(error.message));
  }
}
Run Code Online (Sandbox Code Playgroud)

我假设您的设置需要返回类型声明(否则这很容易!)。暂时删除声明以查看 Typescript 推断的内容确实很有帮助。这就是我得到这个解决方案的方式。

  • 谢谢,这是一个非常好的解释,但我对结果如此无用感到惊讶。对于 Saga,返回值(几乎)总是“void”。效果已经很好地连接起来(即,如果参数错误,“call”会抱怨)。如果您有多个选择器,则无论如何都必须将每个结果转换为其类型。因此,如果我将我的传奇输入为“Generator&lt;any,any,any&gt;”,并将选择器结果定义为“const x: string=yield select(y)”,那么它就像手动操作一样,并且具有更少的额外信息。我错过了什么吗? (2认同)