You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让TypeScript识别React中AbortController的类型收窄?

问题描述

我有一个封装了应用API的类,核心代码如下:

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的逻辑抽成独立函数复用,但遇到了TypeScript类型问题:

尝试1:抽为类内静态函数

调用后TypeScript报错:'abortController.current' is possibly 'null'.ts(18047)

public static abortPreviousAndSetUpNewController = (
  abortController: React.MutableRefObject<AbortController | null>
) => {
  // 取消之前的请求
  if (abortController.current) {
    abortController.current.abort();
  }
  // 为新请求创建控制器
  abortController.current = new AbortController();
};

尝试2:使用is关键字做类型收窄

出现语法错误:A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.

public static abortPreviousAndSetUpNewController = (
  abortController: React.MutableRefObject<AbortController | null>
): abortController is React.MutableRefObject<AbortController>  => {
  // 取消之前的请求
  if (abortController.current) {
    abortController.current.abort();
  }
  // 为新请求创建控制器
  abortController.current = new AbortController();
}

我想让TypeScript知道这个函数执行后,abortController.current的类型已经从AbortController | null变成了AbortController,并且尽可能不用类型断言。

包版本:axios 0.24.0、react 16.14.0


解决方案

方案1:类型谓词函数+返回布尔值

类型谓词需要返回布尔值来告知TypeScript类型收窄结果,我们在函数末尾返回true,同时明确返回类型:

public static abortPreviousAndSetUpNewController = (
  abortController: React.MutableRefObject<AbortController | null>
): abortController is React.MutableRefObject<AbortController> => {
  if (abortController.current) {
    abortController.current.abort();
  }
  abortController.current = new AbortController();
  // 返回true确认类型收窄完成
  return true;
};

调用后TypeScript会自动识别current非空:

static requestData = async (
  abortController: React.MutableRefObject<AbortController | null>
) => {
  Api.abortPreviousAndSetUpNewController(abortController);
  // 此时不会再报current可能为null的错误
  try {
    const response = await instance.get(
      "request/url/string",
      { signal: abortController.current.signal },
    );
    // ...后续逻辑
  } catch (err: unknown) {
    // ...错误处理
  }
};

方案2:函数重载声明

通过重载明确调用前后的类型变化,实现时直接修改并返回原ref:

// 重载声明:输入含null的ref,输出确定有控制器的ref
public static abortPreviousAndSetUpNewController(
  abortController: React.MutableRefObject<AbortController | null>
): React.MutableRefObject<AbortController>;

// 实际实现逻辑
public static abortPreviousAndSetUpNewController(
  abortController: React.MutableRefObject<AbortController | null>
) {
  if (abortController.current) {
    abortController.current.abort();
  }
  abortController.current = new AbortController();
  return abortController;
}

调用时可以直接使用返回的ref,类型自动收窄:

static requestData = async (
  abortController: React.MutableRefObject<AbortController | null>
) => {
  const controllerRef = Api.abortPreviousAndSetUpNewController(abortController);
  try {
    const response = await instance.get(
      "request/url/string",
      { signal: controllerRef.current.signal },
    );
    // ...
  } catch (err) {
    // ...
  }
};

方案3:返回新创建的控制器实例

直接返回函数内创建的控制器,调用时用返回值而非原ref的current,彻底规避类型问题:

public static abortPreviousAndSetUpNewController = (
  abortController: React.MutableRefObject<AbortController | null>
): AbortController => {
  if (abortController.current) {
    abortController.current.abort();
  }
  const newController = new AbortController();
  abortController.current = newController;
  return newController;
};

调用示例:

static requestData = async (
  abortController: React.MutableRefObject<AbortController | null>
) => {
  const controller = Api.abortPreviousAndSetUpNewController(abortController);
  try {
    const response = await instance.get(
      "request/url/string",
      { signal: controller.signal },
    );
    // ...
  } catch (err) {
    // ...
  }
};

以上三种方案都不需要类型断言,能让TypeScript正确识别类型变化,其中方案1最贴合你的需求——直接修改原ref并完成类型收窄。


内容的提问来源于stack exchange,提问作者Harrison

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 20:23:08