Vue 3 严格模式下,可选接口字段为什么会炸:一次上架页 TypeScript 复盘

前端经常会遇到展示文案涉及字符串拼接,例如「更新频率」「数据规模」等字段。JavaScript 里 a + b 很常见;打开 TypeScript 严格检查后,同一行会报「可能为未定义」。根因不是业务写错,而是接口类型标了可选,运行时拼接却当必填字符串用

背景

catalogData 的类型是 API.IPutawayProductDetailRes。其中这些字段都带 ?

ts 复制代码
dataSize?: string;
dataSizeUnit?: string;
updateFrequency?: string;
updateFrequencyUnit?: string;
personalInformation?: number;
authorize?: string;

? 的意思是:值类型是 T | undefined。解构后,变量在类型上就是可选的,不能当「一定存在的 string」做运算。


问题 1:可选字符串直接 + 拼接

报错

"updateFrequencyUnit"可能为"未定义"。

"dataSize"可能为"未定义"。

出错写法

ts 复制代码
updateFrequencyStr: updateFrequency + updateFrequencyUnit
dataSizeStr: dataSize + dataSizeUnit

+ 在两端都是 string 时才是拼接。string | undefined 不能直接参与,因为 undefined + '次' 在类型上不合法(运行时还会拼出 "undefined次")。

修法

用空串兜底,再模板拼接:

ts 复制代码
updateFrequencyStr: `${updateFrequency ?? ''}${updateFrequencyUnit ?? ''}`
dataSizeStr: `${dataSize ?? ''}${dataSizeUnit ?? ''}`

?? 只在 null / undefined 时替换,不会把合法的 0 吃掉。展示层已有 || '-',两边都空时仍显示 -

不要用 ! 断言(updateFrequencyUnit!)来消错:类型过了,接口没回这个字段时仍可能拼出错误文案。


问题 2:函数参数类型比真实字段窄

报错

类型"string | undefined"的参数不能赋给类型"number | undefined"的参数。

出错写法

ts 复制代码
function getIsCover(val?: number): string {
  return val === 1 ? '是' : '否';
}

personalInformation: getIsCover(personalInformation) // number | undefined,OK
authorize: getIsCover(authorize)                      // string | undefined,不匹配

接口里「是否」并不统一:personalInformationnumberauthorize'0' | '1' 这种字符串。函数只收 number,调用就被拦下。

修法

把入参放宽,并把 '1'1 都当成「是」:

ts 复制代码
function getIsCover(val?: number | string): string {
  return val === 1 || val === '1' ? '是' : '否';
}

这是对齐真实契约 ,不是关掉检查。后端 0/1 和 '0'/'1' 混用时,只认一种会在运行时显示错。


问题 3:解构了一个类型上不存在的字段

报错

类型"IPutawayProductDetailRes"上不存在属性"resourceNames"。

组件按 resourceNames 拼「关联数据资源」,但类型里对应的是 resourceId?: string[]。从 props.catalogData 解构 resourceNames,在严格检查下就是「对象上没有这个键」。

修法

先确认键是否存在、值是否为数组,再 join

ts 复制代码
resourceNamesStr:
  'resourceNames' in props.catalogData && Array.isArray(props.catalogData.resourceNames)
    ? props.catalogData.resourceNames.join(',')
    : '-'

'x' in obj 会把后续访问收窄。长期更干净的做法是:类型补上 resourceNames,或展示改用已声明的 resourceId


问题 4:字典 Ref 可能为 undefined

报错

"deliveryTypeOptions.value"可能为"未定义"。

useDict 返回的是 Ref<T[] | undefined>,首屏可能还没拉到字典。.value.find(...) 默认假定 .value 一定是数组。

修法

可选链:

ts 复制代码
deliveryTypeOptions.value?.find((item) => item.value === formData.value.deliveryType)

没有字典时 findundefined,后面的 if (find) 本来就能兜住。


问题 5:修完逻辑后多出来的 unused

去掉拼接后,updateFrequencyUnitList 字典不再使用,eslint 报 assigned but never used。删掉未用的 useDict 即可,避免「为了展示单位去拉字典、实际却拼原始码」。


对照表

现象 类型原因 做法
A + B 报未定义 `T undefined不能直接+`
getIsCover 参数不匹配 接口 0/1 与 '0'/'1' 混用 参数写成 `number
没有 resourceNames 展示字段和类型声明不一致 in + Array.isArray,或改类型/改字段
.value.find 报未定义 Ref 初始可能是 undefined ?.find
unused 字典 重构后引用消失 删掉

可复用的原则

  1. 可选字段先当「可能没有」 ,再决定展示;不要靠 JS 的 undefined + ''
  2. 函数入参跟接口走 ,不要想当然全是 number
  3. 解构前确认键在类型里;运行时多出来的字段要用收窄,不要假装类型有它。
  4. 异步字典、表单实例一律 ?.
  5. 不要用 as / ! 糊过去 ;这次是展示层兜底,所以用 ?? ''|| '-' 就够。

一句话:严格 TypeScript 逼你把「后端可能不给、字典可能没到、0 和 '1' 可能混用」写进代码。修的不是报错文案,是把展示逻辑和接口契约对齐。

相关推荐
EricStone7 小时前
Agent开发学习一:Hello-Agents TypeScript 全栈实现
typescript·node.js·agent
YIAN7 小时前
TS 面试必考题:type 与 interface 的 6 大核心区别,90% 的人答不全
前端·typescript
日光倾2 天前
TypeScript 随手记 —— 1
前端·javascript·typescript
werdedeage2 天前
从一个 loading 到完整状态机:多模型图片编辑流程的前端建模
typescript
JaydenAI2 天前
[TypeScript学习笔记-10]一个特殊且重要的类型:Symbol
typescript·symbol
To_OC3 天前
啃完 TS 工具类型我发现:Pick 和 Omit 原来就是一层窗户纸
前端·面试·typescript
JaydenAI3 天前
[TypeScript学习笔记-07]根据现有类型创建新类型的N种方式
typescript
东风破_3 天前
TypeScript 高级类型进阶:keyof、Exclude、Record 与类型组合思想
前端·后端·typescript
晓说前端3 天前
TypeScript 核心语法应用 —— Vue 3 中的使用(下)
前端·javascript·typescript·类型系统