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

React TypeScript中间件paramsSerializer处TypeScript类型错误求助

解决React TypeScript项目中API中间件的类型错误

问题背景

我正在开发一个React TypeScript项目,已编写API中间件用于将后端返回数据的键转换为驼峰式(camelCase),将发送至后端数据的键转换为蛇形命名(snakeCase),使用js-convert-case包处理键名转换。中间件代码如下:

import Axios from 'axios'
import * as queryString from 'query-string'
import * as jsConvert from 'js-convert-case'
import { cleanParams } from '_utilities/common'
import { REQUEST_TYPES } from './constants'
import { useLoginUser } from '_store/user'

interface IAuth {
  method?: string
  url?: string
  data?: any
  params?: any
  contentType?: string
  isTransformRequestRequired?: boolean
  isAuthroizedToken?: boolean
}

export function apiCaller({
  method = REQUEST_TYPES.GET,
  url = '',
  params = {},
  data = {},
  contentType = 'application/json',
  isAuthroizedToken = true,
  isTransformRequestRequired = true,
}: IAuth) {
  const user = useLoginUser.getState().user
  const setLogout = useLoginUser.getState().setLogout
  const token = user?.data?.token || ''

  let headers
  if (isAuthroizedToken) {
    headers = {
      Authorization: `Token ${token}`,
      'Content-Type': contentType,
    }
  } else {
    headers = {
      'Content-Type': contentType,
    }
  }

  return Axios({
    method,
    url,
    params,
    paramsSerializer: (queryParams) =>
      queryString.stringify(jsConvert.snakeKeys(cleanParams(queryParams))),
    data,
    transformRequest: [
      (reqData) =>
        isTransformRequestRequired ? JSON.stringify(jsConvert.toSnakeCase(reqData)) : reqData,
    ],
    transformResponse: [
      (respData) => {
        if (typeof respData === 'string')
          return jsConvert.camelKeys(JSON.parse(respData), {
            recursive: true,
            recursiveInArray: true,
          })
        else {
          return jsConvert.camelKeys(respData, { recursive: true, recursiveInArray: true })
        }
      },
    ],
    headers: headers,
    responseType: 'json',
    validateStatus: (status) => status >= 200 && status < 300,
  })
    .then(({ data: resp }) => resp)
    .catch((error) => {
      if (error?.response?.status === 401) {
        setLogout()
      } else {
        throw error
      }
    })
}

类型错误详情

在以下代码段出现TypeScript类型错误:

paramsSerializer: (queryParams) =>
          queryString.stringify(jsConvert.snakeKeys(cleanParams(queryParams))),

错误提示:

Argument of type 'object | null' is not assignable to parameter of type 'Record<string, any>'.
  Type 'null' is not assignable to type 'Record<string, any>'.ts(2345)
(alias) snakeKeys(obj: any, opt?: Options | undefined): object | null
export snakeKeys

cleanParams函数代码

export const cleanParams = (obj: Record<string, any>): Record<string, any> => {
  Object.keys(obj).forEach((key) => {
    if (obj[key] === false) {
      delete obj[key]
    } else if (obj[key]) {
      if (key === 'isDisabled') {
        delete obj[key]
        obj.enabled = false
      }
    }
  })
  Object.keys(obj).forEach((key) => {
    if (
      obj[key] === null ||
      obj[key] === '' ||
      obj[key] === undefined ||
      obj[key].length === 0 ||
      (key === 'status' && obj[key] === 0)
    ) {
      delete obj[key]
    }
  })
  Object.keys(obj).forEach((key) => {
    if (obj[key] === true) {
      obj[key] = 1
    }
  })
  return obj
}

解决方案

问题核心是jsConvert.snakeKeys的类型定义标注返回值为object | null,而queryString.stringify要求传入Record<string, any>类型。即便cleanParams返回的是确定的Record<string, any>,TypeScript仍会遵循snakeKeys的类型声明报错,以下是几种解决方式:

方式1:类型断言

直接通过类型断言告诉TypeScript,snakeKeys的返回值不会为null:

paramsSerializer: (queryParams) =>
  queryString.stringify(jsConvert.snakeKeys(cleanParams(queryParams)) as Record<string, any>),

方式2:空值兜底处理

显式处理snakeKeys可能返回null的情况(实际业务中不会触发),用空对象兜底:

paramsSerializer: (queryParams) => {
  const convertedParams = jsConvert.snakeKeys(cleanParams(queryParams));
  return queryString.stringify(convertedParams ?? {});
},

方式3:自定义类型声明(可选)

如果确定snakeKeys在传入非null对象时绝不会返回null,可以创建类型声明文件覆盖原有定义:
在项目中创建src/types/js-convert-case.d.ts,内容如下:

import 'js-convert-case';

declare module 'js-convert-case' {
  export function snakeKeys(obj: Record<string, any>, opt?: Options): Record<string, any>;
}

内容的提问来源于stack exchange,提问作者Asad Ur Rehman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:17:51