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

如何用Zod避免手动类型断言解决Object is possibly 'undefined'错误

问题分析与解决方案

错误原因

TypeScript的数组类型(如T[])默认包含空数组的可能性,Zod的.min(1)约束仅在运行时验证生效,类型推断阶段无法自动将数组类型细化为「至少包含一个元素的非空数组」。

你定义的Grid和Board被Zod推断为number[][]和Cell[][],这两种类型在TypeScript看来既可能是空数组,也可能包含空的子数组,因此board[0]会被标记为「可能是undefined」。

无手动断言的解决方案

方案1:调整Zod验证器以推断非空数组类型

通过z.tuple结合z.rest,让Zod直接推断出「至少包含一个非空子数组的二维数组」类型,TypeScript会自动识别board[0]必然存在:

export const cellValidator = z.object({
  shown: z.boolean(),
  flagged: z.boolean(),
  mine: z.boolean(),
  numNeighbors: z.optional(z.number().nonnegative()),
})
// 修改gridValidator,强制推断为非空二维数组
export const gridValidator = z.tuple([z.array(z.number()).min(1)]).rest(z.array(z.number()).min(1))
// 修改boardValidator,同理
export const boardValidator = z.tuple([z.array(cellValidator).min(1)]).rest(z.array(cellValidator).min(1))
export const positionValidator = z.tuple([
  z.number().nonnegative(),
  z.number().nonnegative(),
])

export type Cell = z.infer<typeof cellValidator>
export type Grid = z.infer<typeof gridValidator> // 推断为 [number[], ...number[][]]
export type Board = z.infer<typeof boardValidator> // 推断为 [Cell[], ...Cell[][]]
export type Position = z.infer<typeof positionValidator>

此时validPosition函数中的board[0].length不会再触发类型错误,因为TypeScript明确知道board的第一个元素一定存在。

方案2:自定义类型守卫缩小类型

如果不想修改Zod验证器,可以编写类型守卫函数,在函数内部缩小board的类型范围:

// 类型守卫:验证二维数组非空且子数组非空
function isNonEmpty2DArray<T>(arr: T[][]): arr is [T[], ...T[][]] {
  return arr.length > 0 && arr[0].length > 0
}

export default function validPosition(board: Grid | Board, position: Position) {
  const [row, col] = position
  // 利用类型守卫告诉TypeScript,board是合法的非空二维数组
  if (!isNonEmpty2DArray(board)) {
    // 因Zod已做运行时验证,此分支实际不会执行
    return false
  }
  const numRows = board.length
  const numCols = board[0].length

  return row >= 0 && row < numRows && col >= 0 && col < numCols
}

方案3:直接指定非空数组类型别名

由于你已通过Zod的.min(1)确保了运行时的数组结构,可以直接覆盖Zod推断的类型,明确指定Grid和Board为非空二维数组:

// 定义非空二维数组类型
type NonEmpty2DArray<T> = [T[], ...T[][]]

export const cellValidator = z.object({
  shown: z.boolean(),
  flagged: z.boolean(),
  mine: z.boolean(),
  numNeighbors: z.optional(z.number().nonnegative()),
})
export const gridValidator = z.array(z.array(z.number()).min(1)).min(1)
export const boardValidator = z.array(z.array(cellValidator).min(1)).min(1)
export const positionValidator = z.tuple([
  z.number().nonnegative(),
  z.number().nonnegative(),
])

export type Cell = z.infer<typeof cellValidator>
export type Grid = NonEmpty2DArray<number> // 直接指定为非空类型
export type Board = NonEmpty2DArray<Cell> // 直接指定为非空类型
export type Position = z.infer<typeof positionValidator>

这种方式最简洁,完全依赖你已有的Zod运行时验证,类型层面直接匹配实际的结构约束。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 06:15:32