使用 Typescript 在 Redux-Saga 中键入生成器函数 [英] Typing generator functions in Redux-Saga with Typescript

查看:90
本文介绍了使用 Typescript 在 Redux-Saga 中键入生成器函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我开始重构我的 React 项目以开始在其中使用 Typescript.我遇到了打字问题,不知道如何解决.

Recently I've started refactoring my React project to start using Typescript in it. I've encountered a problem with typing which I don't know how to overcome.

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

Tech-stack: Typescript, React, Redux, Redux-Saga

我也在使用带有 AirBnB 规则集的 ESLint.

I'm also using ESLint with AirBnB ruleset.

我正在尝试编写一个使用在我的授权服务中定义的异步函数的 saga.下面的代码是我第二次尝试写这个.我已经拆分了 signIn 传奇,以便不混合 yield 返回类型,如 这篇文章.

I'm trying to write a saga which uses async function defined in my authorization service. The code below is my 2nd attempt to write this. I've splitted the signIn saga in order not to mix the yield return types as suggested on the end of this article.

export function* callSignInService(userCredentials: UserCredentialsInterface): 
    Generator<CallEffect, UserAuthDataInterface, UserAuthDataInterface> > 
{
    return yield call(signInService, userCredentials);
}

export function* signIn(action: PayloadAction<UserCredentialsInterface>): 
    Generator<StrictEffect, void, PutEffect> 
{
    try {
        const { payload } = action;
        const userData = yield* callSignInService(payload);

        yield put(signInSuccess(userData));
    } catch (error) {
        yield put(signInFailure(error.message));
    }
}

登录服务的简化版本:

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

问题是我仍然遇到错误:

The problem is I'm still getting an error on:

const userData = yield* callSignInService(payload);

说:

TS2766:无法将迭代委托给值,因为其迭代器的next"方法需要类型UserAuthDataInterface",但包含生成器将始终发送PutEffect".输入SimpleEffect"缺少UserAuthDataInterface"类型的以下属性:用户,令牌

TS2766: Cannot delegate iteration to value because the 'next' method of its iterator expects type 'UserAuthDataInterface', but the containing generator will always send 'PutEffect'.   Type 'SimpleEffect<"PUT", PutEffectDescriptor>' is missing the following properties from type 'UserAuthDataInterface': user, token

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

I understand that the issue is that my signIn generator NextType is set to PutEffect and it expects expression yield signInService(userCredentials) to return PutEffect but that's not my goal. I want to use the signInService to get UserAuthData and then put these data into signInSuccess action (signInSuccess is created using action creator).

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

I've read several resources on how to properly type the generators but still can't get it to work with my example. I found information that redux-saga has or had problems regarding the typing but I am not sure if it's still the case in 2021.

我在这里遗漏了一些明显的东西吗?也许我尝试编写这样的 Saga 存在问题.如果有人可以看看并给我建议如何处理这个问题,我会很高兴.

Am I missing some obvious things here? Maybe there is a problem with my attempt to write such Saga. I would be pleased if someone could take a look and give me an advice how to handle this problem.

推荐答案

第一个泛型类型参数是 saga 中 yield ed 值的类型.换句话说,就是步骤".

The first generic type parameter is the type of the yielded values in the saga. It's the "steps", in other words.

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

Moving callSignInService to a separate function doesn't actually help with the types because we are still yielding that result.

你的 signIn saga 有两个不同类型的步骤——调用 API 和分派动作.泛型需要是两种类型的联合.

Your signIn saga has two steps with distinct types -- calling the API and dispatching an action. The generic needs to be a union of the two types.

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));
  }
}

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

I'm assuming that your settings require an return type declaration (or else this would be easy!). It can be really helpful to remove the declaration temporarily to see what Typescript infers. That's how I got this solution.

这篇关于使用 Typescript 在 Redux-Saga 中键入生成器函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆