打字稿:使用来自 redux-saga 的 call() 类型

Rum*_*nsk 14 yield call typescript redux-saga

如何使用 设置函数的类型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);
        }
    )  
}
Run Code Online (Sandbox Code Playgroud)

这个函数可以像这样使用:

let resp = await apiFetch<ServerResponse>("http://localhost:51317/Task");
Run Code Online (Sandbox Code Playgroud)

通过使用您在上面的代码段中看到的函数,它resp是正确的字符串类型。所以智能感知为我提供了ServerResponse界面的所有属性。

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

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


function* workerRefreshTaskSaga() {
  //I need to call the function here
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用 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");
Run Code Online (Sandbox Code Playgroud)

第一个选项,按预期执行函数,但是respany类型。第二个选项让我感到异常。

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.
Run Code Online (Sandbox Code Playgroud)

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

Nic*_*wer 17

不幸的是, a 的左侧yield 总是有 type 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'); 
Run Code Online (Sandbox Code Playgroud)

只有当您可以假设 redux saga 正在运行您的生成器时,您才能对类型进行预测,并且 typescript 无法说“假设此生成器将由 redux saga 运行(以及包括的所有含义)”。

所以你需要自己添加类型。例如:

const resp: ServerResponse = yield call(apiFetch, 'url');
Run Code Online (Sandbox Code Playgroud)

这确实意味着您有责任使类型正确。由于打字稿只能告诉它是一个any,因此无论您说什么类型,它都会信任您。因此 typescript 可以验证此后的代码是否与 a 正确交互ServerResponse,但如果它实际上不是 a ServerResponse,则 typescript 无法向您指出这一点。

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

const output: ReturnType<typeof someFunction> = yield call(someFunction);
Run Code Online (Sandbox Code Playgroud)

我仍然要知道这ReturnType<typeof someFunction>是正确的,但假设我这样做了,那么如果有人更改 someFunction 的实现以使其返回不同的内容,则输出的类型将更新以匹配。

  • 据我所知,TS 目前不提供输入“yield”返回内容的功能。您认为可以将其添加到语言中吗?生成器和调用“下一个”的事物之间的隐式契约可以变得显式。 (2认同)
  • 看起来像 TS v3.6 [改进了 Generator 的类型能力](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html)。 (2认同)
  • 只是想指出这个答案中可能存在的错误,如果 `someFunction` 是一个函数(而不是类型),那么它应该这样输入:`ReturnType&lt;typeof someFunction&gt;` 而不是 `ReturnType&lt;someFunction&gt;` (2认同)