如何用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
相关产品推荐
相关产品推荐

