告诉 TypeScript 我的类型已更改/缩小的方法是什么?

huangapple go评论104阅读模式
英文:

How do I tell TypeScript that my Type has changed/narrowed?

问题

以下是您要翻译的内容:

  1. 我有一个用于我的应用程序的 API 代码的类:
  2. ```typescript
  3. export class Api {
  4. ...
  5. static requestData = async (
  6. abortController: React.MutableRefObject<AbortController | null>
  7. ) => {
  8. // 如果有先前的请求,取消它
  9. if (abortController.current) {
  10. abortController.current.abort();
  11. }
  12. // 为新请求设置新的控制器
  13. abortController.current = new AbortController();
  14. try {
  15. const response = await instance.get(
  16. "request/url/string",
  17. { signal: abortController.current.signal, },
  18. );
  19. if (response.success) {
  20. // 处理成功的响应
  21. } else {
  22. // 处理已知错误
  23. }
  24. } catch (err: unknown) {
  25. const signal = abortController.current.signal;
  26. if (abortController.current.signal.aborted) {
  27. // 处理被取消的请求
  28. } else {
  29. // 处理未知错误
  30. }
  31. }
  32. };

Axios 实例在之前被定义,使用 axios.create

  1. const instance = axios.create({
  2. baseURL: "base-url-path",
  3. });

我想要重构代码,用于取消请求和创建新的 AbortController

  1. // 如果有先前的请求,取消它
  2. if (abortController.current) {
  3. abortController.current.abort();
  4. }
  5. // 为新请求设置新的控制器
  6. abortController.current = new AbortController();

将其移到自己的函数中(以便在其他地方重用并减少重复的代码)。

尝试 1

我尝试将上面的代码移到类的内部成为一个函数:

  1. public static abortPreviousAndSetUpNewController = (
  2. abortController: React.MutableRefObject<AbortController | null>
  3. ) => {
  4. // 如果有先前的请求,取消它
  5. if (abortController.current) {
  6. abortController.current.abort();
  7. }
  8. // 为新请求设置新的控制器
  9. abortController.current = new AbortController();
  10. };

这样我可以这样做:

  1. static requestData = async (
  2. abortController: React.MutableRefObject<AbortController | null>
  3. ) => {
  4. this.abortPreviousAndSetUpNewController();
  5. try {
  6. const response = await instance.get(
  7. "request/url/string",
  8. { signal: abortController.current.signal, },
  9. );
  10. if (response.success) {
  11. // 处理成功的响应
  12. } else {
  13. // 处理已知错误
  14. }
  15. } catch (err: unknown) {
  16. const signal = abortController.current.signal;
  17. if (abortController.current.signal.aborted) {
  18. // 处理被取消的请求
  19. } else {
  20. // 处理未知错误
  21. }
  22. }
  23. };

但我收到错误消息:'abortController.current' is possibly 'null'.ts(18047)

尝试 2

我还尝试使用 is 关键字
让我考虑使用 is 的示例代码:

  1. export const notNullNorUndefined = <T = any>(
  2. value: T
  3. ): value is NonNullable<T> => value !== null && value !== undefined;

如果一个可能为 null/undefined 的对象被传递给这个函数,如果结果是 true,编译器将接受传递给此函数的对象类型不再是 null | undefined
尝试 2 的代码:

  1. public static abortPreviousAndSetUpNewController = (
  2. abortController: React.MutableRefObject<AbortController | null>
  3. ): abortController is React.MutableRefObject<AbortController | null> => {
  4. // 如果有先前的请求,取消它
  5. if (abortController.current) {
  6. abortController.current.abort();
  7. }
  8. // 为新请求设置新的控制器
  9. abortController.current = new AbortController();
  10. }

但这会返回语法错误:A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.

如何告诉 TypeScript 我的函数 (abortPreviousAndSetUpNewController) 已经改变了 abortController.current,所以它的类型不再是 AbortController | null,而只是 AbortController?我尽量避免断言类型。

请注意,我的软件包版本是:

  • axios - 0.24.0
  • react - 16.14.0
  1. 这是您的翻译。如果您需要更多帮助,请告诉我。
  2. <details>
  3. <summary>英文:</summary>
  4. I have a class that houses my API code for my application:
  5. ```typescript
  6. export class Api {
  7. ...
  8. static requestData = async (
  9. abortController: React.MutableRefObject&lt;AbortController | null&gt;
  10. ) =&gt; {
  11. // If previous request, cancel
  12. if (abortController.current) {
  13. abortController.current.abort();
  14. }
  15. // Set new controller for new request
  16. abortController.current = new AbortController();
  17. try {
  18. const response = await instance.get(
  19. &quot;request/url/string&quot;,
  20. { signal: abortController.current.signal, },
  21. );
  22. if (response.success) {
  23. // Handle successful response
  24. } else {
  25. // Handle known error
  26. }
  27. } catch (err: unknown) {
  28. const signal = abortController.current.signal;
  29. if (abortController.current.signal.aborted) {
  30. // Handle aborted request
  31. } else {
  32. // Handle unknown error
  33. }
  34. }
  35. };

The Axios instance is defined earlier, using axios.create

  1. const instance = axios.create({
  2. baseURL: &quot;base-url-path&quot;,
  3. });

I want to refactor the code that aborts and creates the new AbortController

  1. // If previous request, cancel
  2. if (abortController.current) {
  3. abortController.current.abort();
  4. }
  5. // Set new controller for new request
  6. abortController.current = new AbortController();

into its own function (to be reused elsewhere and reduce the amount of repeated code).

Attempt 1

I tried to move the above code to its own function inside the class:

  1. public static abortPreviousAndSetUpNewController = (
  2. abortController: React.MutableRefObject&lt;AbortController | null&gt;
  3. ) =&gt; {
  4. // If previous request, cancel
  5. if (abortController.current) {
  6. abortController.current.abort();
  7. }
  8. // Set new controller for new request
  9. abortController.current = new AbortController();
  10. };

So that I could do the following:

  1. static requestData = async (
  2. abortController: React.MutableRefObject&lt;AbortController | null&gt;
  3. ) =&gt; {
  4. this.abortPreviousAndSetUpNewController();
  5. try {
  6. const response = await instance.get(
  7. &quot;request/url/string&quot;,
  8. { signal: abortController.current.signal, },
  9. );
  10. if (response.success) {
  11. // Handle successful response
  12. } else {
  13. // Handle known error
  14. }
  15. } catch (err: unknown) {
  16. const signal = abortController.current.signal;
  17. if (abortController.current.signal.aborted) {
  18. // Handle aborted request
  19. } else {
  20. // Handle unknown error
  21. }
  22. }
  23. };

But I get the error: &#39;abortController.current&#39; is possibly &#39;null&#39;.ts(18047)

Attempt 2

I also tried to use the is keyword
Example code that made me consider using is:

  1. export const notNullNorUndefined = &lt;T = any&gt;(
  2. value: T
  3. ): value is NonNullable&lt;T&gt; =&gt; value !== null &amp;&amp; value !== undefined;

If a potentially null/undefined object is passed to this function, if the result is true the compiler accepts that the type of the object passed cannot be null | undefined anymore.
Attempt 2 code:

  1. public static abortPreviousAndSetUpNewController = (
  2. abortController: React.MutableRefObject&lt;AbortController | null&gt;
  3. ): abortController is React.MutableRefObject&lt;AbortController | null&gt; =&gt; {
  4. // If previous request, cancel
  5. if (abortController.current) {
  6. abortController.current.abort();
  7. }
  8. // Set new controller for new request
  9. abortController.current = new AbortController();
  10. }

But this returns syntax errors: A function whose declared type is neither &#39;undefined&#39;, &#39;void&#39;, nor &#39;any&#39; must return a value.

How do I tell TypeScript that my function (abortPreviousAndSetUpNewController) has mutated abortController.current so that its' type is no longer AbortController | null and only AbortController?

I would prefer not to assert the type where possible

Note, my package versions are:

  • axios - 0.24.0
  • react - 16.14.0

答案1

得分: 0

After Jerryh001的提示以及另一个答案的帮助(在下面链接中),我能够找到解决方案:

  1. public static abortPreviousAndSetUpNewController(
  2. abortController: React.MutableRefObject<AbortController | null>
  3. ): asserts abortController is React.MutableRefObject<AbortController> {
  4. // 如果之前有请求,取消
  5. if (abortController.current) {
  6. abortController.current.abort();
  7. }
  8. // 为新请求设置新控制器
  9. abortController.current = new AbortController();
  10. }

这意味着我可以得到所需的代码:

  1. static requestData = async (
  2. abortController: React.MutableRefObject<AbortController | null>
  3. ) => {
  4. this.abortPreviousAndSetUpNewController();
  5. try {
  6. const response = await instance.get(
  7. "request/url/string",
  8. { signal: abortController.current.signal, },
  9. );
  10. if (response.success) {
  11. // 处理成功的响应
  12. } else {
  13. // 处理已知错误
  14. }
  15. } catch (err: unknown) {
  16. const signal = abortController.current.signal;
  17. if (abortController.current.signal.aborted) {
  18. // 处理已中止的请求
  19. } else {
  20. // 处理未知错误
  21. }
  22. }
  23. };

注意:

根据此答案

请记住,TypeScript 不能在断言中使用箭头函数。

该答案提供了一个链接,指向此问题,该问题进一步提到了一个建议
不幸的是,这两个线程都被标记为“已关闭”,所以目前不能在使用asserts关键字时使用箭头函数。

英文:

After Jerryh001's hint and help from another answer (linked below), I was able to get to a solution:

  1. public static abortPreviousAndSetUpNewController(
  2. abortController: React.MutableRefObject&lt;AbortController | null&gt;
  3. ): asserts abortController is React.MutableRefObject&lt;AbortController&gt; {
  4. // If previous request, cancel
  5. if (abortController.current) {
  6. abortController.current.abort();
  7. }
  8. // Set new controller for new request
  9. abortController.current = new AbortController();
  10. }

Which meant that I could have the desired code:

  1. static requestData = async (
  2. abortController: React.MutableRefObject&lt;AbortController | null&gt;
  3. ) =&gt; {
  4. this.abortPreviousAndSetUpNewController();
  5. try {
  6. const response = await instance.get(
  7. &quot;request/url/string&quot;,
  8. { signal: abortController.current.signal, },
  9. );
  10. if (response.success) {
  11. // Handle successful response
  12. } else {
  13. // Handle known error
  14. }
  15. } catch (err: unknown) {
  16. const signal = abortController.current.signal;
  17. if (abortController.current.signal.aborted) {
  18. // Handle aborted request
  19. } else {
  20. // Handle unknown error
  21. }
  22. }
  23. };

Note:

As per this answer:

> keep in mind that TypeScript cannot use arrowFunctions for assertions.

The answer posts a link to this issue which follows on to a suggestion.
Unfortunately, both of these threads are marked as "closed" so for the time-being you can't use arrow functions when using the asserts keyword.

huangapple
  • 本文由 发表于 2023年6月26日 23:09:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76557972.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定