You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

RTK Query自定义baseQuery中如何为API调用添加响应类型

RTK Query自定义baseQuery中如何为API调用添加响应类型

我来帮你搞定这个问题!在RTK Query的自定义baseQuery里,给内部的刷新token请求指定响应类型的思路和你创建mutation时的方式略有不同,下面我给你拆解两种类型安全的实现方式,再也不用依赖as断言啦~

核心思路:给baseQuery调用显式指定泛型参数

RTK的FetchBaseQuery本身是泛型函数,我们在调用它的时候可以直接传入响应数据的类型,TypeScript会自动推导refreshResult的类型,完全不需要手动做类型断言。


方式一:直接在调用baseQuery时指定泛型(最推荐)

这是最直接的方式,针对刷新token这个特定请求,显式告诉TypeScript它的响应结构:

优化后的关键代码片段

// 显式指定泛型:第一个参数是响应数据类型,第二个是错误类型
const refreshResult = await baseQuery<IApiResponse<RefreshTokenResponse>, FetchBaseQueryError>(
    {
        url: API_ROUTES.AUTH.REFRESH_TOKEN,
        method: "POST",
        body: { refreshToken: refreshToken }
    },
    store,
    extraOptions
)

if (refreshResult.data) {
    // 此时refreshResult.data的类型已经是IApiResponse<RefreshTokenResponse>,无需as断言
    // 这里根据你的IApiResponse结构调整:如果是嵌套的data字段就用refreshResult.data.data,直接返回的话用refreshResult.data
    store.dispatch(setNewAccessToken(refreshResult.data.data))
    result = await baseQuery(args, store, extraOptions);
}

完整的baseQueryWithReauth代码(带类型优化)

const baseQueryWithReauth: BaseQueryFn<
    string | FetchArgs,
    unknown,
    FetchBaseQueryError
> = async (args, store, extraOptions) => {
    let result = await baseQuery(args, store, extraOptions)
    const refreshToken = (store.getState() as RootState).auth.refreshToken;
    if (result.error && result.error.status === 401) {
        if (refreshToken) {
            // 给刷新token的请求指定明确的响应类型
            const refreshResult = await baseQuery<IApiResponse<RefreshTokenResponse>, FetchBaseQueryError>(
                {
                    url: API_ROUTES.AUTH.REFRESH_TOKEN,
                    method: "POST",
                    body: { refreshToken: refreshToken }
                },
                store,
                extraOptions
            )

            if (refreshResult.data) {
                // 类型安全,无需手动断言
                store.dispatch(setNewAccessToken(refreshResult.data.data))
                result = await baseQuery(args, store, extraOptions);
            } else {
                console.error("Failed to fetch new access token: ", refreshResult.error)
                store.dispatch(logOut())
            }
        } else {
            console.warn("no refresh token found logging out");
            store.dispatch(logOut())
        }
    }
    return result
}

方式二:封装一个带类型的刷新token请求函数(更易复用)

如果刷新token的逻辑在多个地方用到,你可以把它封装成一个单独的类型安全函数:

// 封装带类型的刷新token请求
const fetchNewToken = async (
    refreshToken: string,
    store: BaseQueryApi,
    extraOptions: Record<string, unknown>
): Promise<BaseQueryResult<IApiResponse<RefreshTokenResponse>, FetchBaseQueryError>> => {
    return baseQuery<IApiResponse<RefreshTokenResponse>, FetchBaseQueryError>(
        {
            url: API_ROUTES.AUTH.REFRESH_TOKEN,
            method: "POST",
            body: { refreshToken }
        },
        store,
        extraOptions
    )
}

// 然后在baseQueryWithReauth里调用这个函数
const refreshResult = await fetchNewToken(refreshToken, store, extraOptions)

关键说明:和mutation中指定类型的区别

你平时在创建mutation时指定响应类型,是在API slice层面定义对外暴露的请求类型;而自定义baseQuery里的刷新token请求是内部自动触发的逻辑,不属于你定义的API slice,所以需要在调用baseQuery时直接指定泛型参数,让TypeScript识别这个内部请求的响应结构。

这样调整后,你的代码不仅类型完全安全,还能避免因手动断言带来的潜在错误,可读性也大大提升了~ 😊

火山引擎 最新活动