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

已添加if块校验,为何TypeScript仍抛出对象可能未定义错误?

问题描述

我在Express服务器中实现了处理购物车更新HTTP请求的控制器函数updateCart,代码如下:

export async function updateCart(
  req: TypedRequestBody<{
    addItem?: {
      productId: string
      variantId: string
      quantity: number
    }
    updateItem?: {
      productId: string
      variantId: string
      quantity: number
    }
    removeItems?: Array<string>
  }>,
  res: Response,
) {
  const {farmerId} = req.params
  let cart = await CartModel.findOne({farmerId})
  if (!cart) cart = await CartModel.create({farmerId})

  if (req.body.addItem) {
    const itemIndex = cart.items.findIndex(item => item.productId.toString() === req.body.addItem.productId)
    if (itemIndex !== -1) throw new createError.UnprocessableEntity('Item already exists in cart')
    cart.items.push(req.body.addItem)
  }

  if (req.body.updateItem) {
    const itemIndex = cart.items.findIndex(item => item.productId.toString() === req.body.updateItem.productId)
    if (itemIndex === -1) throw new createError.UnprocessableEntity('Item does not exists in cart')
    cart.items[itemIndex] = req.body.updateItem
  }

  if (req.body.removeItems) {
    cart.items = cart.items.filter(item => !req.body.removeItems.includes(item.productId.toString()))
  }

  cart = await cart.save()

  return res.send(new HttpResponse(true, {cart}))
}

我已在if块中对req.body.addItem、req.body.updateItem、req.body.removeItems做了存在性校验,但TypeScript编译器仍抛出以下TS2532错误:

  • src/controllers/farmer.ts:288:82 - error TS2532: Object is possibly 'undefined'.
  • src/controllers/farmer.ts:294:82 - error TS2532: Object is possibly 'undefined'.
  • src/controllers/farmer.ts:300:45 - error TS2532: Object is possibly 'undefined'.

补充说明:TypedRequestBody接口定义如下:

import {Query} from 'express-serve-static-core'
import {Request} from 'express'

interface MyRequest extends Request {
  auth?: TokenData
}

export interface TypedRequestBody<T> extends MyRequest {
  body: T
}

请问为何已添加if块校验,TypeScript仍会抛出该错误?

问题原因

核心问题出在TypedRequestBody的定义与TypeScript类型守卫的限制上:

  1. Express原生Request接口中,body属性的默认类型是any/unknown,你定义的TypedRequestBody<T>直接覆盖body类型为T,但TypeScript无法确认运行时req.body的结构完全匹配泛型T的定义。
  2. 当你用if (req.body.addItem)做存在性校验时,TypeScript的类型检查器无法穿透req.body的嵌套属性完成类型窄化——它无法信任req.body本身严格符合你定义的泛型类型,因此仍会认为req.body.addItem等属性可能为undefined。
解决方案

以下是几种可行的修复方式:

1. 解构req.body到局部变量

将req.body赋值给局部变量,让TypeScript能正确识别类型守卫的作用范围:

export async function updateCart(
  req: TypedRequestBody<{
    addItem?: {
      productId: string
      variantId: string
      quantity: number
    }
    updateItem?: {
      productId: string
      variantId: string
      quantity: number
    }
    removeItems?: Array<string>
  }>,
  res: Response,
) {
  const {farmerId} = req.params
  // 提取到局部变量,明确类型
  const requestBody = req.body
  let cart = await CartModel.findOne({farmerId})
  if (!cart) cart = await CartModel.create({farmerId})

  if (requestBody.addItem) {
    const itemIndex = cart.items.findIndex(item => item.productId.toString() === requestBody.addItem.productId)
    if (itemIndex !== -1) throw new createError.UnprocessableEntity('Item already exists in cart')
    cart.items.push(requestBody.addItem)
  }

  if (requestBody.updateItem) {
    const itemIndex = cart.items.findIndex(item => item.productId.toString() === requestBody.updateItem.productId)
    if (itemIndex === -1) throw new createError.UnprocessableEntity('Item does not exists in cart')
    cart.items[itemIndex] = requestBody.updateItem
  }

  if (requestBody.removeItems) {
    cart.items = cart.items.filter(item => !requestBody.removeItems.includes(item.productId.toString()))
  }

  cart = await cart.save()

  return res.send(new HttpResponse(true, {cart}))
}

2. 优化TypedRequestBody接口定义

通过Omit移除原生Request中的body属性,再重新定义,避免类型冲突:

import { Request } from 'express'

interface MyRequest extends Omit<Request, 'body'> {
  auth?: TokenData
}

export interface TypedRequestBody<T> extends MyRequest {
  body: T
}

这样TypeScript会明确知道req.body的类型是你指定的泛型T,而非原生的模糊类型,类型守卫就能正常工作。

3. 使用自定义类型守卫函数

编写类型守卫来严格校验req.body的结构,同时完成类型断言:

// 自定义类型守卫
function isCartUpdateBody(body: unknown): body is {
  addItem?: { productId: string; variantId: string; quantity: number }
  updateItem?: { productId: string; variantId: string; quantity: number }
  removeItems?: string[]
} {
  if (typeof body !== 'object' || body === null) return false
  // 可根据需求添加更严格的字段校验逻辑
  return true
}

export async function updateCart(
  req: Request,
  res: Response,
) {
  const {farmerId} = req.params
  // 先校验请求体结构
  if (!isCartUpdateBody(req.body)) {
    throw new createError.BadRequest('Invalid request body')
  }
  const body = req.body
  let cart = await CartModel.findOne({farmerId})
  if (!cart) cart = await CartModel.create({farmerId})

  // 后续逻辑正常编写,TypeScript已能识别body的属性类型
  if (body.addItem) {
    const itemIndex = cart.items.findIndex(item => item.productId.toString() === body.addItem.productId)
    if (itemIndex !== -1) throw new createError.UnprocessableEntity('Item already exists in cart')
    cart.items.push(body.addItem)
  }

  // ...其余逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 21:54:16