ARTICLE DETAIL

资讯详情

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

Flow 抽象枚举(Abstract Enums):用 `Enum<>` 与 `EnumValue<>` 编写通用枚举工具函数

Flow 抽象枚举(Abstract Enums):用 `Enum<>` 与 `EnumValue<>` 编写通用枚举工具函数 开发工具静态分析代码质量【免费下载链接】flowAdds static typing to JavaScript to improve developer productivity and code quality.项目地址https://gitcode.com/gh_mirrors/flow30/flow点击查看免费下载导读Flow Enums 是 Flow 引入的一组名义化nominal枚举类型其值不会隐式与string等底层表示类型互相转换。本篇文章围绕evals/evals/02_unique_features/enum_007_abstract_enums/prompt.md这一任务展开在不绑定任何具体枚举的前提下基于 Flow 的抽象枚举类型Enum与EnumValue编写三个通用工具函数——enumToArray、safeParseEnum、enumIncludes让同一份代码可以处理任意字符串底层string-backed的 Flow Enum。读完本文你将掌握抽象枚举的泛型签名写法、四大内置方法cast/isValid/members/getName的底层类型定义以及如何在Array.from、??空值合并等场景下安全、类型完备地操作枚举。任务背景一次对任意枚举的抽象访问在 prompt.md 中任务要求完成三件事定义两个字符串枚举用于测试Color成员Red、Green、Blue与Size成员Small、Medium、Large编写接受枚举对象本身作为参数的通用工具函数调用方可以传入任意枚举三个函数分别负责遍历枚举成员、安全解析原始字符串、判断字符串是否对应有效成员。该任务的验收配置位于 config.json其难度标记为hard评分器检查三个关键点源码中必须出现EnumDeclaration节点即真实的enum声明泛型函数签名中必须出现Enum泛型标注泛型函数签名中必须出现EnumValue泛型标注。换句话说考核的核心不是会不会写某个枚举而是能否利用 Flow 的抽象枚举类型写出真正可复用的通用代码——这是本任务与普通枚举用法的根本区别。参考实现golden answer位于 ideal/main.js任务起点 input/main.js 则只保留了一个// TODO: Implement占位。从具体到抽象Enum与EnumValue两个内置类型在 lib/misc.js 中Flow 库定义了抽象枚举的两个核心类型type EnumValue out TRepresentationType extends EnumRepresentationTypes EnumRepresentationTypes $EnumValueTRepresentationType; type Enumout TEnumValue extends EnumValue EnumValue $EnumTEnumValue;其中EnumValue表示任意 Flow 枚举的值如Color.Red。可以通过类型参数收窄到底层表示类型例如EnumValuestring只接受字符串枚举的值EnumValuenumber只接受数字枚举的值Enum表示枚举本身如Color这个对象而不是它的某个成员值。通过类型参数可以进一步约束为其值为特定类型的枚举典型写法就是EnumEnumValuestring——这正是任意字符串枚举的类型表述。两者的底层类型参数都受到EnumRepresentationTypes的约束见 lib/misc.jstype EnumRepresentationTypes string | number | symbol | boolean | bigint;也就是说Flow 枚举的底层表示类型共五种string、number、symbol、boolean、bigint。这也解释了为什么字符串枚举的泛型约束要写成T extends EnumValuestringstring必须落在EnumRepresentationTypes允许的范围内。为什么需要抽象类型如果没有它们你只能针对具体枚举写function f(e: typeof Color) {...}代码被绑定死在一个枚举上。而Enum让你把任意枚举对象当作类型来使用配合EnumValue约束其成员类型从而写出与具体枚举解耦的通用逻辑。测试文件 tests/enums/abstract-enum.js 也印证了这套约束体系E as EnumE、declare const x: EnumE均合法——具体枚举可以赋给EnumEnumboolean、EnumEnumValueboolean是错误用法boolean不是合法的表示类型或成员约束泛型函数内可以安全调用e.members()、e.cast(raw)、e.isValid(raw)、e.getName(val)但不能访问e.A这类具体成员也不能对未知成员做switch——这正是抽象枚举没有具体成员的本质体现。第一步定义被测枚举按任务要求定义两个字符串枚举。字符串枚举是 Flow 的默认枚举形式无需of string子句见 website/docs/enums/defining-enums.md// flow export enum Color { Red, Green, Blue, } export enum Size { Small, Medium, Large, }这里所有成员都采用默认值写法字符串枚举的成员值默认是成员名的字符串形式如Red的底层值为Red也可以通过Red crimson显式指定。成员声明为export后其他模块即可import {Color, Size}使用同时可被typeof提取为枚举对象类型。第二步编写三个通用工具函数参考实现 ideal/main.js 给出了完整答案。核心模式是同一套泛型签名function utilT extends EnumValuestring(e: EnumT): ... { // 通过参数 e 使用 .cast / .isValid / .members() / .getName }T extends EnumValuestring类型变量T代表某个字符串枚举的成员值类型e: EnumT参数e是成员类型为T的枚举对象调用方传入Color或Size即可完成类型推导T Color或T Size。enumToArray遍历成员返回 label/value 数组export function enumToArrayT extends EnumValuestring( e: EnumT, ): Array{label: string, value: string} { return Array.from(e.members(), member ({ label: e.getName(member), value: member.valueOf(), })); }实现要点e.members()返回IteratorTEnumValue官方文档给出的类型签名为members(): IteratorTEnum见 website/docs/enums/using-enums.md。枚举对象本身不可迭代for...in/for...of直接遍历枚举是无效的必须通过.members()才能拿到成员迭代器Array.from(iterable, mapFn)在把迭代器转成数组的同时逐成员映射e.getName(member)把成员值映射回成员名字符串如Color.Red→Redmember.valueOf()将成员值显式转换为底层字符串。这是抽象枚举场景下的关键技巧由于EnumValue不保证具体表示类型不能用member as string直接断言而valueOf()是$EnumValueProto上定义的方法见 lib/misc.js返回TRepresentationType正好适用于泛型上下文。safeParseEnum安全解析原始字符串export function safeParseEnumT extends EnumValuestring( e: EnumT, input: string, ): T | null { return e.cast(input) ?? null; }实现要点e.cast(input)是枚举对象上的方法官方类型签名为cast(input: ?TRepresentationType): TEnum | void见 website/docs/enums/using-enums.md。它安全地把原始字符串转换为枚举值合法时返回对应成员非法时返回void即undefined??空值合并运算符把void归一化为null得到任务要求的T | null返回类型由于cast内部依赖this指向枚举对象若需要把cast作为回调传递官方文档建议包一层箭头函数如strings.map((s) Color.cast(s))本任务的封装形式天然规避了this丢失问题。enumIncludes判断字符串是否为有效成员export function enumIncludesT extends EnumValuestring( e: EnumT, input: string, ): boolean { return e.isValid(input); }实现要点e.isValid(input)的类型签名为isValid(input: ?TRepresentationType): boolean见 website/docs/enums/using-enums.md与cast共享同一张枚举值 → 成员名的反向缓存表只是返回布尔值而非成员本身与cast相同isValid同样依赖this绑定本任务通过参数e直接调用不存在解绑问题。三个函数的使用示例上述函数对Color与Size完全通用const colors enumToArray(Color); // [{label: Red, value: Red}, {label: Green, value: Green}, {label: Blue, value: Blue}] const maybeColor safeParseEnum(Color, Green); // Color.Green const nothing safeParseEnum(Color, Pink); // null const hasLarge enumIncludes(Size, Large); // true const hasXxl enumIncludes(Size, XXL); // false得益于泛型签名enumToArray(Size)、safeParseEnum(Size, Small)等调用会自动把T推导为Size无需任何类型断言。底层原理库定义与解析器如何支撑这套 API库层面的方法契约在 lib/misc.js 中$EnumProto完整定义了枚举对象的方法签名type $EnumProtoTEnum, TEnumValue, TRepresentationType { cast(this: TEnum, input: ?TRepresentationType): void | TEnumValue, getName(this: TEnum, input: TEnumValue): string, isValid(this: TEnum, input: ?TRepresentationType | TEnumValue): boolean, members(this: TEnum): IteratorTEnumValue, __proto__: null, }几个值得注意的细节this: TEnum标注说明四个方法都要求this必须是枚举对象本身——这正是解绑后调用会报错的类型层面原因cast/isValid接受?TRepresentationType即可为空因此传入null或undefined也不会触发类型错误members返回IteratorTEnumValue与enumToArray中Array.from的配合完全一致__proto__: null意味着枚举对象不继承Object.prototype所以E.toString()这类调用在类型层面就会报错测试用例见 tests/enums/methods.js。解析器层面的支持Flow 的 Rust 移植版解析器在 rust_port/crates/flow_parser/src/enum_parser.rs 中负责把enum声明解析为EnumDeclarationAST 节点枚举体Body由成员列表构成成员可以是StringMember、NumberMember、BooleanMember、BigIntMember等显式初始化形式也可以是DefaultedMember默认形式。这与字符串枚举成员可省值、数字枚举不可省值的文档规则见 website/docs/enums/defining-enums.md一一对应——字符串枚举默认值是成员名因此Red可省略赋值数字枚举不允许省值因为中间成员的增删会改变后续成员的值。类型检查器的约束类型层面抽象枚举的合法性由检查器强制保证见 tests/enums/abstract-enum.js 与对应期望输出 tests/enums/enums.expEnumE与typeof E可以互相赋值说明抽象类型与具体枚举之间保持名义等价关系EnumEnumValuestring | number合法但EnumEnumValueboolean报错——表示类型必须来自EnumRepresentationTypes对抽象枚举e访问e.A会报错Cannot get non-existent method因为抽象类型没有具体成员switch (val)对未知成员值会报错——枚举的类型检查要求穷尽成员而抽象场景下成员集合不可知。这些测试文件就是本任务评分逻辑EnumDeclarationEnumEnumValue三类 AST 检查所依赖的底层能力的直接证据。版本与运行前提本仓库为 Flow 官方仓库镜像项目描述Adds static typing to JavaScript to improve developer productivity and code quality。Flow Enums 自 Flow 0.134 起可用本任务使用的Enum/EnumValue抽象枚举 API 为后续版本新增能力请以当前仓库 Changelog.md 与 website/docs/enums/index.md 记录为准文件顶部需要// flow注释或项目级flowconfig开启枚举语法才会被解析枚举是 Flow 的运行时特性cast、members、getName等方法需要flow-remove-types或 Babel 插件如仓库中的 packages/babel-plugin-transform-flow-enums在编译时做转换纯类型擦除不足以支持这些方法在 TypeScript 中EnumValue/Enum的等价能力缺失TS 枚举是结构性的、且缺少Enum对象级抽象类型因此本文的泛型方案是 Flow 独有能力迁移 TS 时需改写为联合类型 工具函数重载。小结围绕抽象枚举这一任务本文完整实现了三个通用工具函数并沿仓库证据链任务配置 → 参考实现 → 库定义 → 测试用例 → 解析器解释了其原理函数使用的枚举方法返回类型关键语义enumToArraymembers()getName()valueOf()Array{label, value}枚举本身不可迭代必须经members()遍历safeParseEnumcast()??T \| nullcast返回void用??归一化为nullenumIncludesisValid()boolean与cast共享反向缓存表掌握EnumTEnumValue与TEnumValue extends EnumValueTRepresentationType这套泛型签名后你可以在任何 Flow 项目中复用这套工具把任意字符串枚举的序列化、解析、校验逻辑收敛为少量可测试的纯函数。赞分享开发工具静态分析代码质量【免费下载链接】flowAdds static typing to JavaScript to improve developer productivity and code quality.项目地址https://gitcode.com/gh_mirrors/flow30/flow点击查看免费下载相关推荐Flow 数字枚举Number Enum实战用 of number 与穷尽 switch 编写 HTTP 状态码分类器Flow 数字枚举Number Enum实战用 of number 与穷尽 switch 编写 HTTP 状态码分类器 本指南以 Flow 官方评测用例开发工具静态分析代码质量PDF补丁丁完整指南批量修复PDF书签、一键合并图片为PDF的免费工具箱PDF补丁丁完整指南批量修复PDF书签、一键合并图片为PDF的免费工具箱 PDF 书签一改名就提示无法打开文档或者文档一点开就偷偷弹出网页PDF 补丁桌面应用文档TypeScript枚举类型详解数字枚举与字符串枚举的完整用法TypeScript枚举类型详解数字枚举与字符串枚举的完整用法 TypeScript枚举类型是TypeScript中一个强大的特性它允许我们定义一组命名的常文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表