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

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

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

问题

以下是您要翻译的内容:

我有一个用于我的应用程序的 API 代码的类:

```typescript
export class Api {
  ...

  static requestData = async (
    abortController: React.MutableRefObject<AbortController | null>
  ) => {
    // 如果有先前的请求,取消它
    if (abortController.current) {
      abortController.current.abort();
    }
    // 为新请求设置新的控制器
    abortController.current = new AbortController();
    try {
      const response = await instance.get(
        "request/url/string",
        { signal: abortController.current.signal, },
      );
      if (response.success) {
        // 处理成功的响应
      } else {
        // 处理已知错误
      }
    } catch (err: unknown) {
      const signal = abortController.current.signal;
      if (abortController.current.signal.aborted) {
        // 处理被取消的请求
      } else {
        // 处理未知错误
      }
    }
  };

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

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

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

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

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

尝试 1

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

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

这样我可以这样做:

static requestData = async (
  abortController: React.MutableRefObject<AbortController | null>
) => {
  this.abortPreviousAndSetUpNewController();
  try {
    const response = await instance.get(
      "request/url/string",
      { signal: abortController.current.signal, },
    );
    if (response.success) {
      // 处理成功的响应
    } else {
      // 处理已知错误
    }
  } catch (err: unknown) {
    const signal = abortController.current.signal;
    if (abortController.current.signal.aborted) {
      // 处理被取消的请求
    } else {
      // 处理未知错误
    }
  }
};

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

尝试 2

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

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

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

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

但这会返回语法错误: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

这是您的翻译。如果您需要更多帮助,请告诉我。

<details>
<summary>英文:</summary>

I have a class that houses my API code for my application:

```typescript
export class Api {
  ...

  static requestData = async (
    abortController: React.MutableRefObject&lt;AbortController | null&gt;
  ) =&gt; {
    // If previous request, cancel
    if (abortController.current) {
      abortController.current.abort();
    }
    // Set new controller for new request
    abortController.current = new AbortController();
    try {
      const response = await instance.get(
        &quot;request/url/string&quot;,
        { signal: abortController.current.signal, },
      );
      if (response.success) {
        // Handle successful response
      } else {
        // Handle known error
      }
    } catch (err: unknown) {
      const signal = abortController.current.signal;
      if (abortController.current.signal.aborted) {
        // Handle aborted request
      } else {
        // Handle unknown error
      }
    }
  };

The Axios instance is defined earlier, using axios.create

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

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

    // If previous request, cancel
    if (abortController.current) {
      abortController.current.abort();
    }
    // Set new controller for new request
    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:

  public static abortPreviousAndSetUpNewController = (
    abortController: React.MutableRefObject&lt;AbortController | null&gt;
  ) =&gt; {
    // If previous request, cancel
    if (abortController.current) {
      abortController.current.abort();
    }
    // Set new controller for new request
    abortController.current = new AbortController();
  };

So that I could do the following:

  static requestData = async (
    abortController: React.MutableRefObject&lt;AbortController | null&gt;
  ) =&gt; {
    this.abortPreviousAndSetUpNewController();
    try {
      const response = await instance.get(
        &quot;request/url/string&quot;,
        { signal: abortController.current.signal, },
      );
      if (response.success) {
        // Handle successful response
      } else {
        // Handle known error
      }
    } catch (err: unknown) {
      const signal = abortController.current.signal;
      if (abortController.current.signal.aborted) {
        // Handle aborted request
      } else {
        // Handle unknown error
      }
    }
  };

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:

export const notNullNorUndefined = &lt;T = any&gt;(
  value: T
): 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:

public static abortPreviousAndSetUpNewController = (
  abortController: React.MutableRefObject&lt;AbortController | null&gt;
): abortController is React.MutableRefObject&lt;AbortController | null&gt;  =&gt; {
  // If previous request, cancel
  if (abortController.current) {
    abortController.current.abort();
  }
  // Set new controller for new request
  abortController.current = new AbortController();
}

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的提示以及另一个答案的帮助(在下面链接中),我能够找到解决方案:

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

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

  static requestData = async (
    abortController: React.MutableRefObject<AbortController | null>
  ) => {
    this.abortPreviousAndSetUpNewController();
    try {
      const response = await instance.get(
        "request/url/string",
        { signal: abortController.current.signal, },
      );
      if (response.success) {
        // 处理成功的响应
      } else {
        // 处理已知错误
      }
    } catch (err: unknown) {
      const signal = abortController.current.signal;
      if (abortController.current.signal.aborted) {
        // 处理已中止的请求
      } else {
        // 处理未知错误
      }
    }
  };

注意:

根据此答案

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

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

英文:

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

  public static abortPreviousAndSetUpNewController(
    abortController: React.MutableRefObject&lt;AbortController | null&gt;
  ): asserts abortController is React.MutableRefObject&lt;AbortController&gt; {
    // If previous request, cancel
    if (abortController.current) {
      abortController.current.abort();
    }
    // Set new controller for new request
    abortController.current = new AbortController();
  }

Which meant that I could have the desired code:

  static requestData = async (
    abortController: React.MutableRefObject&lt;AbortController | null&gt;
  ) =&gt; {
    this.abortPreviousAndSetUpNewController();
    try {
      const response = await instance.get(
        &quot;request/url/string&quot;,
        { signal: abortController.current.signal, },
      );
      if (response.success) {
        // Handle successful response
      } else {
        // Handle known error
      }
    } catch (err: unknown) {
      const signal = abortController.current.signal;
      if (abortController.current.signal.aborted) {
        // Handle aborted request
      } else {
        // Handle unknown error
      }
    }
  };

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:

确定