【免费下载链接】optique
Type-safe combinatorial CLI parser for TypeScript
Optique 是一个面向 TypeScript 的类型安全组合式 CLI 解析器(type-safe combinatorial CLI parser)。这份速查表聚焦最常被用到的六大组合子——object()、or()、merge()、optional()、multiple()、map(),用最少的代码讲清楚每个函数"做什么、类型如何变化、什么时候该选它",帮你在构建子命令、共享选项、重复参数等场景时快速选对组合子。
📌 一屏速查表:六大组合子怎么选
| 组合子 | 一句话定位 | 结果类型 | 所在模块 |
|---|---|---|---|
object() | 把命名字段组成结构化对象 | 对象(字段名→值) | @optique/core/constructs |
or() | 互斥备选,按顺序逐个尝试 | 联合类型(可收窄) | @optique/core/constructs |
merge() | 把多个解析器合并成一个对象 | 合并后的对象 | @optique/core/constructs |
optional() | 允许字段缺失 | T \| undefined | @optique/core/modifiers |
multiple() | 收集重复出现为数组 | 数组T[] | @optique/core/modifiers |
map() | 变换解析出的值 | 映射后的类型U | @optique/core/modifiers |
记忆口诀:object 按名分组、or 选一个、merge 组合并组、optional 允许缺席、multiple 收集重复、map 变换结果。
1️⃣ object():多数 CLI 结构的地基
object()接收一个"字段名 → 解析器"的映射,返回结构化对象,是几乎所有 CLI 应用的基础构件。
const serverConfig = object({ name: argument(string({ metavar: "NAME" })), port: option("-p", "--port", integer()), verbose: option("-v", "--verbose"), });关键要点:
- 无需手写类型注解,
InferValue<typeof serverConfig>会自动推导出完整对象类型。 - 第一个参数可传标签(如
object("Network", {...})),帮助文本中会显示为分组标题。 - 整体优先级取组成解析器中的最高值,因此内嵌的 command 字段会优先被尝试。
源码入口:packages/core/src/constructs.ts。
2️⃣ or():互斥模式与子命令分发
or()按顺序尝试每个备选,直到某一个成功,是"server 模式 / client 模式"以及子命令结构的标准做法。
const gitLike = or( command("add", object({ type: constant("add"), files: multiple(argument(string())), })), command("commit", object({ type: constant("commit"), message: option("-m", "--message", string()), })), );关键要点:
- 配合
constant()字段会形成 TypeScript 判别联合(discriminated union),可对type字段写类型安全的switch。 - 单次
or()调用最多 15 个分支,更多时拆成嵌套分组。 - 想让"一个都不选"也合法并得到
undefined,用optional()包住整个or()。 - 错误消息可通过
errors.noMatch选项自定义,会根据上下文(期望的是 option / command / argument)自动适配措辞。
源码入口:packages/core/src/constructs.ts;可运行示例见 examples/patterns/subcommands.ts 与 examples/patterns/mutually-exclusive-options.ts。
3️⃣ merge():模块化复用选项组
merge()把多个"产生对象"的解析器合并为一个解析器,是实现"同一选项组在多个命令间复用"的关键。
const globalOptions = object({ verbose: option("-v", "--verbose"), config: option("-c", "--config", string()), }); const deployParser = merge(globalOptions, deployOptions);关键要点:
- 最常见模式是
merge(globals, or(...)):把全局选项提到每个子命令分支之外,避免在每条命令里重复声明。 - 不限于
object():withDefault()、map()等产生对象的解析器同样可以合并,类型会智能推断。 - 与
or()一样受 15 个参数上限约束;也支持标签参数,在帮助文本中整组显示。 - 可用
optional()包裹某个对象组,让整个分组变成"可选功能组",不与必填参数冲突。
源码入口:packages/core/src/constructs.ts;实战配方见 examples/patterns/shared-options.ts。
4️⃣ optional():显式允许"未提供"
optional()让任何解析器变成可选:匹配成功就返回其值,失败则返回undefined,不报错、不消耗输入。
const backupConfig = object({ source: argument(string({ metavar: "SRC" })), compress: optional(option("-c", "--compress", choice(["gzip", "bzip2"]))), });关键要点:
- 类型从
T变为T | undefined,编译器会强制你处理"值可能不存在"的情况,避免运行时误判。 - 当"值缺失"本身有语义时使用它;如果你想要一个兜底值而不是
undefined,应改用withDefault()。 - 它是把
or()变成"全部可选"的标准搭配:optional(or(flagA, flagB))。
源码入口:packages/core/src/modifiers.ts。
5️⃣ multiple():重复参数收集成数组
multiple()允许解析器多次匹配,并把所有结果收集到数组中,适合多个输入文件、include 路径、环境变量等可重复选项。
const parser = object({ files: multiple(argument(string()), { min: 1, max: 5 }), includes: multiple(option("-I", "--include", string())), });关键要点:
- 未提供任何匹配时返回空数组而非失败,因此重复选项天然"可选",取值前无需判空。
- 约束选项
{ min, max }控制最少与最多出现次数,例如"恰好 2~4 个文件"。 - 常与
map()搭配:把数组转成Set(去重小写)或 key-value 对象。
源码入口:packages/core/src/modifiers.ts。
6️⃣ map():变换解析结果
map()用映射函数变换解析结果,同时保留原解析逻辑,适合类型转换、格式归一化、派生字段等场景。
const parser = object({ upperName: map(option("-n", "--name", string()), (name) => name.toUpperCase()), tags: map(multiple(option("-t", "--tag", string())), (tags) => new Set(tags.map((tag) => tag.toLowerCase()))), });关键要点:
- 变换函数必须返回新值,不能原地修改输入(延迟提示解析时输入可能是共享占位引用,原地修改会污染后续解析)。
- 支持链式流式写法,如
option("--port", integer()).map((v) => v + 1).withDefault(3001),与独立函数写法等价。 - 常与
or()搭配:把每个分支的 flag 映射成字符串标签(map(flag("--verbose"), () => "verbose" as const)),让联合类型更可读。
源码入口:packages/core/src/modifiers.ts。
🔗 典型组合套路:组合子的威力在"套娃"
| 场景 | 典型组合 |
|---|---|
| 子命令 + 全局选项 | merge(globals, or(cmdA, cmdB, ...)) |
| 全部可选的互斥项 | optional(or(flagA, flagB)) |
| 详细度等级(数 flag 次数) | optional(map(multiple(flag("--verbose")), (v) => v.length)) |
| 重复参数转 Set / 对象 | map(multiple(option("-t", string())), ...) |
| 条件性选项组 | merge(withDefault(object({...}), { flag: false }), globalOptions) |
组合之后,帮助文本、shell 补全、man 页面都由同一个解析器定义生成,无需手工同步维护。
组合子产生的错误信息(如or()的noMatch)同样是结构化 Message:选项名、值、metavar 会在终端中着色高亮,管道或 CI 环境下自动降级为带引号的纯文本,方便脚本消费。更多细节见 docs/concepts/messages.md。
🧭 选型决策流:拿不准时按这个顺序问自己
- 想把一堆字段按名字组织成对象 →
object() - 要"几个里选一"的互斥模式或子命令 →
or() - 想把同一组选项在多处复用 →
merge() - 某字段允许不出现 →
optional() - 某参数可以出现多次 →
multiple() - 想在解析后变换值 →
map()
如果结果类型"长得不对",先想"少套了哪个组合子",而不是重写整个解析器——这正是组合式解析器"复杂行为由简单积木组合"的核心思想。
📚 参考资料
- 构造组合子官方文档:docs/concepts/constructs.md
- 修饰组合子官方文档:docs/concepts/modifiers.md
- 核心实现源码:packages/core/src/constructs.ts、packages/core/src/modifiers.ts
- 可运行模式示例目录:examples/patterns/,推荐从 conditional-options.ts、dependent-options.ts、verbosity-levels.ts 入手
【免费下载链接】optique
Type-safe combinatorial CLI parser for TypeScript
相关推荐
如何跨 1000 多个网站查出社交账号?Social-Analyzer 完整实操指南
如何跨 1000 多个网站查出社交账号?Social Analyzer 完整实操指南 手里只有一个用户名,想知道这个人出现在哪些网站上,逐个打开上千家站点去猜不
后端网络安全网页爬虫Pixelle-Video终极指南:AI全自动短视频创作从入门到精通
Pixelle Video终极指南:AI全自动短视频创作从入门到精通 Pixelle Video是一款革命性的AI全自动短视频引擎,能够将你的创意想法一键转化为
人工智能AI 应用媒体生成视频视频处理语音数字人MCP 服务Reactor Core 组合操作符终极指南:掌握 zip、combineLatest、merge 三大核心工具
Reactor Core 组合操作符终极指南:掌握 zip、combineLatest、merge 三大核心工具 在反应式编程的世界中,Reactor Core
后端异步编程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考