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

如何让TypeScript识别异步API请求捕获的Response类型错误以避免类型断言?

如何让TypeScript识别异步API请求捕获的Response类型错误以避免类型断言?

我有一些通过fetch API和Promise实现的API方法(比如下面的login方法):

export const handleResponse = async (response: Response, session?: Session): Promise<Response> => {
    // 检查错误响应
    if (!response.ok) {
        if ([401, 403].includes(response.status) && session?.auth) {
            session?.logout();
        }
        return Promise.reject(response);
    }
    return response;
}

export const login = async (username: string, password: string) => {
    const response = await fetch("/auth/login", {
        method: "POST",
        body: JSON.stringify({ username, password })
    });

    return (await handleResponse(response));
}

我用await语法调用这些方法:

try {
    const response = await API.client.login(username, password);
    console.log(await response.json());
    // 处理响应
} catch (error) {
    console.log(await error.json());
    // 处理错误
}

但TypeScript无法识别catch里error参数的类型,默认把它标记为unknown,导致我必须用类型断言才能访问它的属性(比如.json()方法),这很麻烦。

如何解决这个问题?


这个问题我之前处理过类似的场景,核心原因是TypeScript默认会把Promise reject的内容推断为unknown类型,所以你在catch块里拿不到明确的类型提示。下面给你几个实用的解决方案,都能避免繁琐的类型断言:

方案一:自定义API错误类(最推荐)

我们可以创建一个专门的错误类,把Response对象封装进去,这样TypeScript就能明确知道catch到的错误类型:

首先定义错误类:

// 自定义API错误类,携带响应对象
class ApiError extends Error {
  constructor(public response: Response) {
    super('API请求失败');
    // 修复ES6错误类的原型链问题
    Object.setPrototypeOf(this, ApiError.prototype);
  }
}

然后修改handleResponse函数,把原来的Promise.reject(response)改成抛出这个错误类的实例:

export const handleResponse = async (response: Response, session?: Session): Promise<Response> => {
    if (!response.ok) {
        if ([401, 403].includes(response.status) && session?.auth) {
            session?.logout();
        }
        // 抛出自定义错误,替代Promise.reject
        throw new ApiError(response);
    }
    return response;
}

最后在调用的时候,用类型守卫判断错误类型:

try {
    const response = await API.client.login(username, password);
    console.log(await response.json());
    // 处理响应
} catch (error) {
    // 用instanceof做类型守卫,TypeScript会自动推断error的类型
    if (error instanceof ApiError) {
        console.log(await error.response.json());
        // 这里可以安全访问response的所有属性,不需要断言
    } else {
        // 处理其他意外错误,比如网络故障
        console.error('发生未知错误:', error);
    }
}

这种方式的好处是类型安全,还能在错误类里扩展更多属性(比如错误码、错误信息),非常灵活。

方案二:提前解析错误响应并抛出结构化对象

如果不想自定义错误类,也可以在handleResponse里直接解析错误响应,抛出一个结构化的对象,让TypeScript明确它的类型:

首先定义错误响应的类型:

// 根据你的API实际返回的错误结构定义
interface ApiErrorData {
  message: string;
  code?: number;
  // 其他错误字段
}

interface RequestError {
  status: number;
  data: ApiErrorData;
}

然后修改handleResponse:

export const handleResponse = async (response: Response, session?: Session): Promise<Response> => {
    if (!response.ok) {
        if ([401, 403].includes(response.status) && session?.auth) {
            session?.logout();
        }
        // 提前解析错误响应,抛出结构化对象
        const errorData = await response.json() as ApiErrorData;
        throw { status: response.status, data: errorData } as RequestError;
    }
    return response;
}

调用时可以用类型守卫或者直接断言(类型守卫更安全):

try {
    const response = await API.client.login(username, password);
    console.log(await response.json());
} catch (error) {
    // 用类型守卫判断是否是我们抛出的RequestError
    if (typeof error === 'object' && error !== null && 'status' in error && 'data' in error) {
        const requestError = error as RequestError;
        console.log(requestError.status, requestError.data.message);
    } else {
        console.error('未知错误:', error);
    }
}

这种方式省去了自定义类的步骤,适合简单场景,但类型守卫的代码会稍微繁琐一点。

方案三:给Promise指定完整的类型(不推荐,灵活性差)

如果你不想修改现有逻辑,也可以给login函数明确指定返回的Promise类型,把reject的类型也声明出来:

// 明确指定login返回的Promise,resolve是Response,reject也是Response
export const login = async (username: string, password: string): Promise<Response> => {
    const response = await fetch("/auth/login", {
        method: "POST",
        body: JSON.stringify({ username, password })
    });

    return (await handleResponse(response));
}

然后在catch里用类型断言,但其实这只是让你心里有数,TypeScript还是会把error视为unknown,所以你还是需要手动断言:

catch (error) {
    // 这里必须用断言,还是有点繁琐,所以不推荐
    console.log(await (error as Response).json());
}

这种方式本质上还是用了类型断言,只是提前声明了类型,不如前两种方案优雅。

总结一下,最推荐的是第一种自定义错误类的方式,既保证类型安全,又能灵活扩展错误信息,完全避免了繁琐的类型断言。

备注:内容来源于stack exchange,提问作者moltarze

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 13:59:30