ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flow `$ObjMapConst` 现代化迁移实战:用原生映射类型(Mapped Type)重写废弃工具类型

Flow `$ObjMapConst` 现代化迁移实战:用原生映射类型(Mapped Type)重写废弃工具类型 Flow$ObjMapConst现代化迁移实战用原生映射类型Mapped Type重写废弃工具类型【免费下载链接】flowAdds static typing to JavaScript to improve developer productivity and code quality.项目地址: https://gitcode.com/gh_mirrors/flow30/flow$ObjMapConst是 Flow 早期提供的一个对象映射工具类型用于把对象类型的每个属性统一映射成同一种类型。随着 Flow 对映射类型mapped type的原生支持逐渐成熟这类工具类型先后被标记废弃并最终移除仓库中大量代码因此需要现代化modernize。本文以 Flow AI Evals 中的modernize_013_objmapconst评估任务为主线完整讲解该迁移的输入输出、底层语义、判分机制与仓库源码佐证读完你可以独立完成$ObjMapConst以及同类$ObjMap/$ObjMapi到映射类型的等价改写并理解这类迁移是如何被自动化验证的。从一行任务说起modernize_013_objmapconst 的目录结构与设计理念该评估用例位于 evals/evals/05_code_generation/modernize_013_objmapconst/属于 Flow AI Evals 套件中05_code_generation按规格编写新类型化代码类别。整个用例只有四个文件文件作用prompt.md给模型的任务描述input/main.js迁移前的起始代码包含待现代化的$ObjMapConstideal/main.js参考解法gold patch 的来源config.json元数据与额外的 AST 判分器任务描述只有一句话Modernize the code inmain.js.这不是文档偷工减料而是设计原则使然。evals 套件的 README 明确写道prompt 只描述要做什么绝不透露在 Flow 里该怎么写以避免把答案喂给被评测的模型。因此任务说明刻意保持极简真正的技术载体是input/与ideal/两个目录。config.json的元数据进一步揭示了本用例的技术主题{ metadata: { name: modernize_013_objmapconst, category: code_generation, tags: [flow, modernize, code_generation, removed, mapped_type, objmapconst], difficulty: medium } }注意tags中的两个关键词removed被移除的特性与mapped_type映射类型——这正是本次迁移的一体两面旧语法$ObjMapConst已被移除新语法是原生映射类型。为什么需要现代化$ObjMapConst的生命周期与废弃依据要理解这个迁移任务先要看清$ObjMapConst在 Flow 版本演进中的位置。仓库根目录的 Changelog.md 记录了完整的脉络诞生$ObjMapConstO, T作为内置类型被加入语义上等价于$ObjMapO, () T即把对象O的每个属性值都映射为固定类型T。收紧与豁免在experimental.new_merge默认开启后导出包含$ObjMap这类复杂投影的类型会触发[invalid-exported-annotation]错误而当时新增的$KeyMirror与$ObjMapConst是少数豁免于该限制的类型因此它们一度成为替代$ObjMap的推荐写法。正式废弃Flow 0.209 起$Call、$ObjMap、$ObjMapi、$ObjMapConst被标记废弃。官方给出的替代方案就是条件类型conditional type与映射类型mapped type并提示可以通过开启deprecated-type这条 Flow lint 让这些废弃用法直接报错。彻底移除$ObjMap的支持被完全移除。Changelog 给出了向后兼容的 shim 建议type $ObjMapO, F {[K in keyof O]: $CallF, O[K]};同时指出在这个映射类型版本中可选属性的O[K]会包含void与旧行为存在细微差异。由于$ObjMapConst本质上是$ObjMap的特化固定返回类型它同样落在被清理的范围内——这也是本用例被打上removed标签的原因。迁移前后对照input/main.js 与 ideal/main.js迁移任务的起点 input/main.js 完整内容如下/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * flow */ type Form { email: string, password: string, }; type FieldErrors $ObjMapConstForm, ?string; export function firstError(errors: FieldErrors): ?string { return errors.email ?? errors.password; }参考解法 ideal/main.js 只改动了关键的一行——第 15 行的类型定义type FieldErrors {[_K in keyof Form]: ?string};其余代码原封不动Form结构、firstError函数、??空值合并逻辑全部保留。这正是现代化类任务的特点只做最小必要的语法迁移不改变程序行为与业务逻辑。对照关系一目了然维度旧写法新写法类型别名type FieldErrors $ObjMapConstForm, ?stringtype FieldErrors {[_K in keyof Form]: ?string}语义将Form每个属性映射为?string遍历Form的键集合每个键映射为?stringAST 节点GenericTypeAnnotationid 为$ObjMapConstObjectTypeMappedTypeProperty依赖已移除的内置工具类型原生语言特性核心语法拆解$ObjMapConstO, T与{[K in keyof O]: T}的等价关系语义等价性$ObjMapConstForm, ?string的含义是构造一个新对象类型键与Form完全相同但每个属性的值类型统一为?string。映射类型{[K in keyof Form]: ?string}表达的是同一件事keyof Form取Form的键联合类型即email | password[K in ...]遍历该键集合为每个键生成一个属性冒号右侧是统一的属性值类型?stringFlow 中?T即T | null | void。在 Flow 中?string已经包含null与void因此firstError中errors.email ?? errors.password的返回类型?string依旧成立函数体无需任何改动——这与$ObjMapConst时代的行为完全一致。常数值映射在仓库测试中的印证这种把所有属性映射为同一类型的用法在仓库测试中很常见。tests/mapped_types/issue-2674.js 就是一个教科书式的例子type MapO {[K in keyof O]: FOO}; type B Map{ FOO: null }; declare const b: B; b.FOO as FOO; // ok b.FOO as BAR; // error b.FOO BAR; // error映射结果对每个键都产生常量字面量类型FOO且赋值被拒绝——这正好说明映射类型不仅能表达统一映射为?string还能表达更精确的字面量常量映射表达力覆盖并超越$ObjMapConst。_K命名惯例理想解法中使用了_K作为遍历变量而不是常见的K。这是因为该遍历变量在等号右侧根本用不到右侧是固定类型?string以下划线开头命名未使用的类型参数是 Flow 社区的通用惯例可以避免严格模式下的未使用告警也向读者明确传达此处的键只是占位的意图。对比同仓库的 tests/mapped_types/identity.jstype IdentityMapO {[K in keyof O]: O[K]}可以看到当右侧需要用到键时使用K并配合O[K]索引访问类型。关于可选属性与精确性迁移时有一个容易忽视的语义细节Changelog 在移除$ObjMap时专门提醒过映射类型版本中可选属性的O[K]会包含void。对本用例而言这并不构成问题——目标类型?string本来就包含了void但如果在迁移$ObjMapO, F这类更一般的场景时目标类型不含void就需要留意可选属性语义的变化。另外映射类型会保留输入对象的精确性exactness语义。tests/exact/objmap.js 验证了这一点type ExactThing { a: 1 }; type Map1To2T extends 1 2; type MappedThing {[K in keyof ExactThing]: Map1To2ExactThing[K]}; const works: MappedThing {a: 2}; // 正常 const doesError: MappedThing {a: 3}; // 报错a 必须是 2 const shouldntWork: MappedThing {a: 2, b: 1}; // 报错精确对象不允许多余键这与 Changelog 中$ObjMap保留输入类型的精确性的修复记录#7642一脉相承说明迁移后精确对象的校验行为保持一致。判分机制AST 级 grader 如何确保真的迁移了一个迁移任务光能通过类型检查是不够的——如果模型只是把$ObjMapConst换成any照样能编译通过但这显然是作弊。因此该用例在 config.json 中声明了两个额外的 AST 级判分器grading: { graders: [ { type: contains_ast_node_type, query: ObjectTypeMappedTypeProperty }, { type: ast_query, selector: .type \GenericTypeAnnotation\ and .id?.name \$ObjMapConst\, negate: true } ] }正向断言必须出现映射类型节点第一个 gradercontains_ast_node_type断言最终代码的 AST 中必须存在ObjectTypeMappedTypeProperty节点——这是 Flow 解析器对映射类型属性的标准 AST 节点类型。其实现位于 evals/graders/contains_ast_node_type.sh本质是对ast_query.sh的薄封装把查询参数包装成选择器.type ObjectTypeMappedTypeProperty。反向断言旧语法必须消失第二个 grader 是ast_query选择器.type GenericTypeAnnotation and .id?.name $ObjMapConst且negate: true——即 AST 中不得再出现以$ObjMapConst为名字的泛型类型标注节点。两个断言一正一反精确锁定了迁移方向既要用上新语法又要彻底清除旧语法。判分器的底层原理核心实现位于 evals/graders/ast_query.shAST$($FLOW_BIN ast $FILE 2/dev/null || true) MATCH_COUNT$(echo $AST | jq [.. | objects | select($SELECTOR)] | length ...)流程是先用flow ast把目标文件解析成 JSON 形式的完整 AST再用jq在整棵 AST 树中递归搜索满足选择器的节点统计命中数量--negate时命中数大于 0 即为失败。这意味着判分不依赖正则文本匹配而是语义层面的验证——即使开发者用不同的空白、换行或属性顺序书写映射类型只要 AST 结构正确就能通过。判分器的组合基线 额外根据 evals/compile_swebench.py 中的类别基线配置05_code_generation类别的每个用例自动附带一组卫生判分器file_modified目标文件必须被修改、flow_check必须零 Flow 错误通过类型检查、no_flowfixme禁止新增$FlowFixMe抑制注释、no_any禁止引入any、no_commonjs禁止退化为 CommonJS 语法外加no_extra_flow_errors阈值 0即代码应一次通过类型检查与no_tsc禁止调用 TypeScript 编译器。config.json中声明的两个 AST 判分器叠加在这些基线上共同组成最终的 TAP 格式判分脚本。同系列迁移矩阵$ObjMap/$ObjMapi/$ObjMapConstmodernize_013_objmapconst不是孤例它是 05_code_generation 目录下modernize系列中的一员。与其相邻的 011、012 用例构成了一组完整的工具类型迁移矩阵用例旧写法input新写法idealmodernize_011_objmaptype FieldHistory $ObjMapFormValues, V(V) ArrayVtype FieldHistory {[K in keyof FormValues]: ArrayFormValues[K]}modernize_012_objmapitype LabeledFields $ObjMapiFields, K, V(K, V) [K, V]type LabeledFields {[K in keyof Fields]: [K, Fields[K]]}modernize_013_objmapconsttype FieldErrors $ObjMapConstForm, ?stringtype FieldErrors {[_K in keyof Form]: ?string}三者分别对应三种映射能力$ObjMapO, F按函数F逐个映射属性值对应{[K in keyof O]: FO[K]}其中O[K]是索引访问类型indexed access type$ObjMapiO, F映射时同时拿到键K与值V对应{[K in keyof O]: [K, O[K]]}这类同时引用键值的写法$ObjMapConstO, T所有属性统一映射为常量类型T对应{[K in keyof O]: T}。可见映射类型是一种统一的、表达能力更强的原生语法可以完整覆盖这三个工具类型的所有使用场景——这正是 Flow 移除它们并以映射类型作为正统写法的原因。每个用例的config.json都配置了类似的正向断言新 AST 节点 反向断言旧GenericTypeAnnotation不存在的双重判分结构只是id?.name分别指向$ObjMap、$ObjMapi、$ObjMapConst。仓库佐证映射类型的解析器实现与测试解析器层面的支持映射类型在 Flow 的 ESTree AST 中表现为对象类型ObjectTypeAnnotation下的一种特殊属性节点ObjectTypeMappedTypeProperty。packages/flow-parser/tests/MappedType-test.js 对该节点进行了系统测试涵盖基础形态{[key in keyof O]: O[key]}的解析与打印readonly/readonly/-readonly修饰符的保留对应节点的variance与varianceOp字段键重映射语法{[K in keyof T as K]: T[K]}对应nameType字段。这些字段说明映射类型不仅支持keyof遍历还能表达方差修饰与键重映射语法能力远超早期的$ObjMap家族。类型检查层面的测试除了上述精确性与常量映射测试tests/mapped_types 目录还包含分布性distributivity、可选属性[key in keyof O]?、参数化映射ParameterizedIdO extends {...}等大量用例共同构成映射类型的回归测试矩阵。阅读这些测试可以帮助迁移者确认自己的改写在不同边界情况下可选属性、泛型参数、精确对象、字面量类型的行为是否符合预期。本地复现与验证运行这个 eval如果你想在本地亲手验证这个迁移任务的完整闭环可以按以下步骤操作要求 Python 3.9、Node.js/npm 与含jq/grep/cmp/patch的 POSIX shell 环境# 首次运行会自动安装 flow-bin提供预编译的 flow 二进制无需从源码构建 npm install # 编译该用例并应用参考解法gold patch后执行全部判分器 make validate ARGS--eval modernize_013_objmapconst--eval支持子串或 glob 过滤也可以换用make dry-runvalidate的别名。判分结果会汇总到build/swebench/results.json。若所有 grader 通过说明ideal/main.js的参考解法确实满足类型检查零错误、不包含any/$FlowFixMe/CommonJS、AST 中存在ObjectTypeMappedTypeProperty且不存在$ObjMapConst。想基于自己的 flow 二进制验证可传--flow-bin /path/to/flow或环境变量FLOW_BIN。迁移要点清单等价改写$ObjMapConstO, T直接改写为{[K in keyof O]: T}右侧不引用键时遍历变量惯例上命名为_K。保持行为不变迁移只改类型定义不触碰函数体?string本身含null | void??空值合并逻辑无需调整。留意可选属性差异一般场景下映射类型中O[K]对可选属性会包含void目标类型不含void时需额外确认。精确对象语义保留映射类型保留输入对象的精确性多余键依然报错。不要用any绕过判分器的no_any与 AST 反向断言会同时拦截偷懒解法迁移必须是语法层面的真实替换。举一反三$ObjMap、$ObjMapi分别对应{[K in keyof O]: FO[K]}与{[K in keyof O]: 引用 K 与 O[K] 的写法}三者可一次性纳入同一套现代化改造计划。对于维护 Flow 类型库的开发者而言将$ObjMapConst等废弃工具类型迁移到原生映射类型不仅是消除deprecated-typelint 告警的手段更是让代码贴合 Flow 当前语言方向、减少对历史兼容层依赖的正确路径。而modernize_013_objmapconst这个 eval 的价值在于它用一套可自动判分的评估用例把这条迁移路径固化为可度量、可复现的标准答案。【免费下载链接】flowAdds static typing to JavaScript to improve developer productivity and code quality.项目地址: https://gitcode.com/gh_mirrors/flow30/flow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表