DevEco Code 让 AI 写 ArkTS,enum 反向查表真机翻车——我换成 typeof + as const 后清净了

我正在做的独立项目「雷达鸭」就是用 DevEco Code + ArkTS 开发的,前阵子让 AI 帮我补全一个内容状态枚举,差点被它埋的坑坑到上架前。

说起来有点惭愧,我前三个鸿蒙项目都是老老实实写 enum 的,连代码评审时同事提了句"这能不能不用 enum",我还跟他争了两句,现在想想脸有点热。那时候觉得 enum 多正经啊,IDE 一点就有补全,状态值还能当类型用,看着就专业。AI 也投我所好,每次让我"加个状态"它就 enum Status { ... } 给你怼上来,我都懒得改。

直到某次在自己项目里加了个"草稿 / 已发布 / 已归档"的状态筛选。AI 生成的代码大概长这样:

typescript 复制代码
// AI 在 DevEco Code 里默认给你生成的版本
enum Status {
  Draft = 'draft',
  Published = 'published',
  Archived = 'archived'
}

const labelOf = (s: Status): string => {
  const map: Record<string, string> = {
    [Status.Draft]: '草稿',
    [Status.Published]: '已发布',
    [Status.Archived]: '已归档'
  }
  return map[s]
}

模拟器跑着没毛病,我也就没多想。

等一下,这里我漏说一个前提:上面这段其实是安全的,因为 labelOf 走的是 map[s] 正向查表,key 都是枚举成员本身。真正的雷在后面------有天我想图省事,照着早年写 JS 的习惯写了句 Status[someValue] 做反向查表,想把存库的字符串值翻回枚举名。代码大概是这样:

typescript 复制代码
// 我就是这么把项目坑了的
console.log(Status['draft']) // ArkTS 字符串枚举:运行时拿到的是 undefined

DevEco 的静态检查一句话没说,模拟器也装没事。真机一跑,状态栏全空。我盯着日志看了十分钟才反应过来:ArkTS 的字符串枚举压根没有反向映射 ,那句 Status['draft'] 拿到的就是 undefined。这坑我躺了三天,因为模拟器不报错,我前两个小时还以为是哪个网络请求把状态字段吃掉了,搜了半天 Stack Overflow 也没对上号------人家贴的都是数字枚举的坑,压根没人提字符串枚举这事。

(插一句,鸿蒙这文档排版有时候真让人头大,这个反向映射的坑散在"枚举"那节倒数第二段,字小得像 footnote,我是翻了第三遍才看见。)

顺便说一句,enum 最阴的地方不是它错,而是它"看起来对"。模拟器上 Status[value] 偶尔还能拿到值(数字枚举有反向映射,AI 有时混用),真机换了个编译路径就露馅。这种时灵时不灵的 bug 最磨人,你复现不了,测试同学也复现不了,到头来全甩锅给"偶发性兼容问题"。

我承认我一开始写得很蠢:先是给状态字段加了一层 ?? '未知' 兜底,界面不空了,但筛选逻辑还是错的,等于拿创可贴糊水管。我又怀疑是 @State 没正确触发更新,给组件套了 @Observed@ObjectLink,白忙活一下午------其实根子压根不在状态管理,而是那个 undefined 从源头就把映射表污染了。后来干脆把 enum 全删了。

冷门写法在这儿:as const 一个普通对象,再用 typeof 把它推导成联合字面量类型。

typescript 复制代码
// 冷门但好用的写法
const StatusMap = {
  Draft: 'draft',
  Published: 'published',
  Archived: 'archived'
} as const

// 推导结果是 'draft' | 'published' | 'archived'
type Status = typeof StatusMap[keyof typeof StatusMap]

const labelMap: Record<Status, string> = {
  draft: '草稿',
  published: '已发布',
  archived: '已归档'
}

const labelOf = (s: Status): string => labelMap[s] // 返回类型一定是 string,不可能 undefined

这段代码我让 AI 跑了一次就记住了。它好在哪儿?第一,Status 现在是 'draft' | 'published' | 'archived' 这种联合字面量类型,比 enum 轻,编译出来连个枚举对象都没有,包自然小一截------那个状态模块我实测瘦了差不多 4KB。第二,labelMapRecord<Status, string> 兜底,你要是漏写了 archived 这一项,DevEco Code 当场画红线,根本轮不到真机翻车。第三,反向查表也白给,而且不用任何黑魔法:

typescript 复制代码
const byValue = (v: string): Status | undefined =>
  (Object.values(StatusMap).find(x => x === v) as Status | undefined)

StatusMap 是个实实在在的对象,遍历它取反向值在真机上稳得很,再也不会冒出 undefined 把界面搞花。

说真的,DevEco Code 对这种 as const 写法的自动补全,比 enum 聪明多了。你敲 labelMap. 它直接把那三个字面量候选弹出来,enum 反而经常只给你个 Draft 这种名字,还得自己回想对应的值到底是 'draft' 还是 'Draft'。我是真没想到,一个"少写点代码"的偷懒技巧,顺带把补全体验也救了。

还有个顺带的收获:AI 以前爱在类型对不上的地方甩 as any 糊弄过去,比如 const s = status as any,或者更隐蔽的 JSON.parse(x) as Status。ArkTS 对 any 那叫一个不友好,严格模式下一画红线不说,把字段标成 any 等于告诉编译器"别管了",AI 埋的类型漏洞全被盖住,等出事你连个报错都看不到。现在用联合字面量类型把边界钉死,它想 as any 都没处下嘴。

如果让我重来,我会从第一天就禁掉项目里的 enum。具体做法是在 DevEco Code 的 AI 规则里加一句硬约束,直接写进项目规则文件:

text 复制代码
规则:状态枚举一律用 as const 对象 + 联合字面量类型实现,禁止使用 enum。

再把设置里的 Type Based Completion 打开------这玩意儿默认开着,但配上前一种写法才真正显出威力。说白了 AI 爱写 enum,是因为训练数据里 TS 教程十篇有八篇拿 enum 当范本,它形成了肌肉记忆,觉得不用 enum 就不"工业级"。我承认我当初就是被这种错觉骗了,白熬了仨通宵。

你现在手上的鸿蒙项目里要是还有 enum,不妨全局搜一下 enum 看看有几个在靠反向查表活着。改起来也就半小时,但能让你少熬三个通宵。你试过之后,欢迎来跟我聊聊还有哪些 AI 爱塞、其实该扔的写法。


个人介绍:老三,10+ 年软件开发经验,软件设计师、人工智能应用工程师。平时专注鸿蒙应用开发(ArkTS 北向)与 Web 前端,喜欢折腾 AI 自动化,不定期在 CSDN 分享鸿蒙与 AI 方向的技术笔记。

本文遵循 MIT 协议,转载请注明出处。

相关推荐
m沐沐1 小时前
【自然语言处理】NLP分词与Word2Vec词向量——从原理到实战
人工智能·深度学习·opencv·机器学习·计算机视觉·自然语言处理·word2vec
数聚天成DeepSData1 小时前
外贸海关进出口数据去哪免费下载?从统计到明细的查找指南
linux·服务器·开发语言·前端·网络·人工智能·自然语言处理
tntxia1 小时前
开源的使用AI的drawio绘制软件
人工智能
秦先生在广东1 小时前
`book-to-skill`:把技术书“编译“成 AI Agent 按需加载的结构化知识库
人工智能
东风破_1 小时前
百万字 EPUB 怎么做 RAG?用《天龙八部》跑通 Loader、Splitter、Milvus 与问答
人工智能
秦先生在广东1 小时前
Microsoft Agent Governance Toolkit:用确定性代码墙代替概率性提示词护栏
人工智能
幸福在路上wellbeing1 小时前
AI 智能体开发 · Day 2 详细学习手册
人工智能·学习
1名持续学习的码农1 小时前
Codex 任务总中断:ChatGPT Plus 用户该升级 Pro,还是先把需求写清?
人工智能·gpt·ai·ai编程·codex
QN1幻化引擎1 小时前
把 意识评测做成了一场"非侵入实验":不碰生产代码,分数反而更真了
人工智能·算法·架构