Skip to content

feat(@effect/cli): add withOptionalValue combinator to Options#6221

Open
Zelys-DFKH wants to merge 1 commit intoEffect-TS:mainfrom
Zelys-DFKH:feat/cli-optional-value
Open

feat(@effect/cli): add withOptionalValue combinator to Options#6221
Zelys-DFKH wants to merge 1 commit intoEffect-TS:mainfrom
Zelys-DFKH:feat/cli-optional-value

Conversation

@Zelys-DFKH
Copy link
Copy Markdown

Closes #6182

The `--color[=WHEN]` / `--watch[=mode]` / argparse `nargs='?'` pattern: a flag that works bare with a sensible default, or accepts an explicit value. `@effect/cli` had no way to express this; any non-boolean option always required a trailing value.

`withOptionalValue(fallback)` fills that gap. The flag still must appear unless combined with `.optional()` or `.withDefault()`; only the value portion becomes optional:

```ts
const watch = Options.choice("watch", ["first-failure", "until-done"] as const).pipe(
Options.withOptionalValue("first-failure"),
Options.optional
)
// --watch → Some("first-failure")
// --watch until-done → Some("until-done")
// --watch=until-done → Some("until-done")
// (absent) → None
```

The implementation stays entirely inside `@effect/cli`. `Single` gains one field (`optionalValueDefault: Option`); the combinator is a standard `dual` that sets it via `modifySingle`. The three existing combinators that call `modifySingle` (`withAlias`, `withDescription`, `withPseudoName`) each thread the new field so chaining cannot silently clear it. The two "Expected a value following option" sites in `parseCommandLine` now emit an empty values array when `optionalValueDefault` is set, and `parseInternal` returns the fallback directly for an empty array. `getUsageInternal` wraps the type hint in brackets, so `--level []` appears in `--help` output.

12 new tests in `test/Options.test.ts` cover: supplied value, bare flag fallback, absent flag error, mid-args with and without a trailing value, composition with `optional()` and `withDefault()`, alias order independence (both orderings), `--flag=value` equals syntax, the canonical `choice` use case from the issue, and an integer option. 173 tests pass.

Thanks for the library. Happy to contribute.

@Zelys-DFKH Zelys-DFKH requested a review from IMax153 as a code owner May 7, 2026 02:07
@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog May 7, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

🦋 Changeset detected

Latest commit: 6e7696b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@effect/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Discussion Ongoing

Development

Successfully merging this pull request may close these issues.

Support CLI options with optional values in @effect/cli

1 participant