【问题标题】:TypeScript: Use types on call() from redux-sagaTypeScript:在 redux-saga 的 call() 上使用类型
【发布时间】:2019-10-23 11:58:59
【问题描述】:

如何使用call()设置函数的类型?

我有这个功能:

export function apiFetch<T>(url: string): Promise<T> {
    return fetch(url).then(response => 
        {
            if (!response.ok) throw new Error(response.statusText)
            return response.json().then(data => data as T);
        }
    )  
}

这个函数可以这样使用:

let resp = await apiFetch<ServerResponse>("http://localhost:51317/Task");

通过使用上述代码中的函数,resp 是正确的字符串类型。所以智能感知为我提供了ServerResponse 接口的所有属性。

但是,此函数必须在来自 redux-saga 的工作人员内部调用,这不允许异步函数:

function* refreshTaskSaga():any {
    yield takeEvery("TASK_REFRESH", workerRefreshTaskSaga);
}


function* workerRefreshTaskSaga() {
  //I need to call the function here
}

我尝试使用 yield + call 来调用它,正如redux-saga 文档所说:

a) let resp = yield call(apiFetch, "http://localhost:51317/Task");
b) let resp = yield call(apiFetch<ServerResponse>, "http://localhost:51317/Task");

第一个选项,按预期执行函数,但是resp 具有any 类型。 第二个选项给我一个异常。

No overload matches this call.
  The last overload gave the following error.
    Argument of type 'boolean' is not assignable to parameter of type '{ context: unknown; fn: (this: unknown, ...args: any[]) => any; }'.ts(2769)
effects.d.ts(499, 17): The last overload is declared here.

知道调用它的正确语法并且不会丢失类型吗?

【问题讨论】:

  • call 来自哪里?
  • 我的错误。我要更新这个问题。 Call来自redux-saga

标签: typescript call redux-saga yield


【解决方案1】:

不幸的是,yield 的左侧总是 类型为 any。这是因为生成器函数原则上可以用任何值恢复。 Redux saga 在运行生成器时以可预测的方式运行,但没有什么能阻止某人编写其他代码,这些代码会逐步执行您的 saga 并为您提供与您生成的内容无关的值,例如:

const iterator = workerRefreshTaskSaga();
iterator.next();
// You might have been expecting a ServerResponse, but too bad, you're getting a string.
iterator.next('hamburger'); 

只有当你可以假设 redux saga 正在运行你的生成器时,你才能对类型进行预测,并且 typescript 没有办法说“假设这个生成器将由 redux saga 运行(以及包括)”。

因此,您需要自己添加类型。例如:

const resp: ServerResponse = yield call(apiFetch, 'url');

这确实意味着您有责任确保类型正确。由于 typescript 只能判断它是any,因此无论您说的类型是什么,它都会信任您。所以 typescript 可以验证下面的代码是否与ServerResponse 正确交互,但如果它实际上不是ServerResponse,那么 typescript 无法向您指出这一点。

为了获得更多类型安全,我经常做的一件事是使用ReturnType,例如:

const output: ReturnType<typeof someFunction> = yield call(someFunction);

我仍然知道ReturnType&lt;typeof someFunction&gt; 是正确的,但假设我这样做了,那么如果有人更改了 someFunction 的实现以使其返回不同的东西,输出的类型将被更新以匹配。

【讨论】:

  • 我了解 TS 目前不提供键入从 yield 返回的内容的功能。您认为可以将其添加到语言中吗?生成器和调用“下一个”的事物之间的隐含契约可以明确化。
  • 看起来像 TS v3.6 improved the type ability for Generator
  • 我本以为我可以自己投射这个,并且使用ReturnType 感觉就像我能得到的一样。但即使使用此处描述的相同代码,我仍然会收到错误消息。 let response: ReturnType&lt;typeof apiFunction&gt; = yield call(apiFunction, action.payload, action.meta);Type 'unknown' is not assignable to type 'Promise&lt;IRequestResponse&lt;ResponsePayload&gt;&gt;'
  • 只是想在这个答案中指出一个可能的错误,如果someFunction 是一个函数(不是一个类型),它应该这样输入:ReturnType&lt;typeof someFunction&gt; 而不是ReturnType&lt;someFunction&gt;
【解决方案2】:

阅读:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html,我意识到我们可以将yield类型设置为Generator Type中的第三个参数

import { AnyAction } from "redux";
import { call, put, fork, takeLatest, StrictEffect } from "redux-saga/effects";
import { apiRequest } from "api/requests";
import { setAuthenticationLoader, setLoginError, setToken } from "./actions";
import { sagaTypes } from "./types";
import { LoginResponse } from "api/requests/authentication";

export function* requestLogin(
  action: AnyAction
): Generator<StrictEffect, any, LoginResponse> {
  const setError = (err?: any) => put(setLoginError(err));
  yield put(setAuthenticationLoader(true));
  yield setError();
  try {
    const data = yield call(apiRequest.authentication.login, action.payload);
    if (!data.token) setError(data);
    else yield put(setToken(data.token));
  } catch (err) {
    yield setError(err);
  } finally {
    yield put(setAuthenticationLoader(false));
  }
}

function* watchLoginRequest() {
  yield takeLatest(sagaTypes.REQUEST_LOGIN, requestLogin);
}

export const authenticationSagas = [fork(watchLoginRequest)];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-31
    • 1970-01-01
    相关资源
    最近更新 更多