AI补上的空数组可能掩盖接口故障
文章指出,编程助手可能用 data ?? [] 消除类型错误,却把数据缺失与上游故障都表现为空列表。审查这类修复时,应结合接口契约和异常处理路径,明确失败应如何暴露。
AI 深度解读
AI 把类型报错修好了,页面也能打开,结果用户的项目列表一直是空的。问题可能就藏在一行看似稳妥的 data ?? [],或者一个返回空数组的 catch 里:请求失败被包装成了「没有数据」。
作者提醒,空值兜底决定的是系统失败时如何表现,不能只当成让代码通过检查的小修补。读这篇最有用的地方,是学会在审查 AI 生成代码时,把合法的空结果与被吞掉的异常分开。
一行空数组,怎么把问题藏进页面
原文给出的典型场景是:AI 正在把个人资料页接到项目列表服务上。OpenAPI 契约声明项目字段存在,但响应使用了可选包装层,生成的类型又允许它是 undefined。
到了 projects.map,类型检查不通过。AI 很容易补一个 ?? []:左侧是 null 或 undefined,就用空数组继续渲染。
代码通过检查,并不代表接口约定已经理清。这里至少有三个不同的问题:包装层是否可选、项目字段是否可选,以及请求有没有成功。一个空数组可能把它们全部盖住。
不过,不能把吞错的责任全算到 ?? [] 上。作者也指出,多数生成客户端遇到 HTTP 请求失败时会抛异常,并不会直接返回 undefined。真正掩盖故障的地方,往往是前面另有一个 catch,把异常转换成了空数组。
审查重点不是有没有空值运算符,而是失败在哪里被变成了正常返回值。
为什么「没有项目」可能比报错更难排查
正常抛出的错误通常会留下调用栈、状态码,以及浏览器 Network 面板里的失败请求。这些信息能帮助开发者定位问题。
空列表则可能让用户看到「还没有项目」。用户会以为自己没有创建过,也就不会反馈;如果异常已经被捕获,又没有上报,错误追踪系统也可能保持安静。
这里需要区分两件事:吞掉异常不一定会抹掉 Network 面板里的失败记录,但它会切断错误继续向上暴露的路径。页面不显示错误,监控又没收到异常,团队就更难及时发现故障。
这也是作者担心的地方:演示时页面能渲染,代码看起来很稳,线上却可能长期提供错误的信息。原文没有给出事故样本或发生频率,这是一种失效机制分析,不能据此判断 AI 代码有多大比例存在此类问题。
什么情况下,空值兜底才合理
判断标准是:空值是否属于接口约定中的有效业务状态。
搜索没有匹配项,接口返回空数组,这是一个完整答案。功能开关的响应为了兼容旧客户端,允许省略新增字段,也可以合理设置默认值。它们与网络故障、HTTP 状态错误不是同一类情况。
作者建议把两条路径分开:传输和状态失败沿错误路径处理,只对契约明确允许缺失的字段设置默认值。
如果上游明确用 null 表示「未找到」,可以把这个约定写成具名分支,让审查者直接看到意图:
const NOT_FOUND = null; const projects = response === NOT_FOUND ? [] : response.projects;
要看的是 response === NOT_FOUND:它只把约定好的 null 转为空列表。这段代码的前提是,上游确实遵守该约定;它也没有替 response.projects 增加任何校验。
可选链同样适用这个原则。对明确可选的值使用可选链没有问题,但如果接口保证存在的响应结构,需要连续穿过多层可选链才能读取,就该回头检查类型和实际响应,而不是继续补运算符。
我的判断
我赞同把兜底当成产品行为来审查。对列表页来说,「加载成功但为空」与「加载失败」会直接影响用户下一步操作:前者可能引导创建,后者应该解释失败并提供重试。
但原文提供的是代码审查经验,没有对照实验,也没有证明哪种写法能降低多少故障率。它对「通常不要捕获后返回空数组」的建议,也需要结合数据的重要程度判断。原文允许有意且有文档说明的降级,只是反对异常悄悄消失。
与到处添加 ?? [] 相比,明确错误路径更容易保留诊断信息;与一律禁止空值兜底相比,依据契约处理可选字段又更符合业务需要。
该保留的是失败与空结果的区别,不是强迫所有空值都报错。
还要留意类型来源。如果 OpenAPI 契约、生成类型和实际响应互相矛盾,组件里的默认值只能缓解表象,不能消除这个矛盾。原文没有说明具体客户端和生成配置,因此无法进一步判断该修改契约还是生成方式。
能拿来做什么
- 审查 AI 接入列表接口的改动时,把新加的
?? []与上游调用一起看,确认undefined来自合法可选字段,还是失败后的替代返回值。 - 优先检查返回
[]、{}或空字符串的catch。要求 AI 说明这些返回值代表什么产品行为;没有明确约定时,保留异常传播或上报,并让页面显示错误和重试入口。 - 给前端列表补两种不同的测试:客户端成功返回空数组时显示空状态,客户端因网络错误而拒绝时显示错误状态和重试处理。只测试空列表,验证不了吞错问题。
- 遇到生成类型与接口承诺不一致时,先核对可选包装层和实际响应,再决定是否兜底。不要把「类型检查通过」当成接口已经接对的证据。