Skip to content

Support a multiple Modifier for Arguments (Variadic Flags and Positionals) #258

Description

@LandonSchropp

Describe the feature

Today an argument can hold at most one value. There is no supported way to accept a flag repeatedly,
such as --environment A=1 --environment B=2, or to accept several positionals of the same kind,
such as lint fileA.ts fileB.ts. The only workaround is to reach into ctx.args._ and parse them by
hand, which loses typing, validation, and help output.

This has come up several times before:

Each prior proposal solved a slice of the problem, usually positionals only, and modeled
multiplicity as a new type, which does not compose with the existing string, enum, and
positional types or with required.

Proposal

I propose we add an optional multiple?: boolean modifier that any argument can set. It is
orthogonal to type and to the existing required field, and it defaults to false, so nothing
changes for current definitions.

Together, multiple and required form a 2×2 matrix that expresses the full range of cardinality.
These correspond to the same four cases regular expressions express with ?, *, +, and a bare
match:

required multiple cardinality regex analog parsed type
false (default) false (default) zero or one ? T | undefined
true false exactly one (bare) T
false true zero or more * T[]
true true one or more + T[]

Because both fields already default to false, this is purely additive. Existing commands keep the
exact behavior they have now, and required keeps working as before, since it simply becomes the
multiple: false row of the matrix.

One constraint applies to positionals: only the last positional may be multiple, since a variadic
positional consumes the remaining tokens. At most one positional can set it, and declaring a
multiple positional before another positional should be a definition-time error.

Examples

A repeatable flag (zero or more):

export default defineCommand({
  args: {
    environment: {
      type: "string",
      alias: "e",
      multiple: true,
      description: "Environment variable as NAME=VALUE (repeatable)",
    },
  },
  run({ args }) {
    // mycommand -e A=1 -e B=2
    args.environment; // => ["A=1", "B=2"], typed as string[]
  },
});

A variadic positional (one or more):

export default defineCommand({
  args: {
    files: {
      type: "positional",
      required: true,
      multiple: true,
      description: "Files to lint",
    },
  },
  run({ args }) {
    // lint fileA.ts fileB.ts
    args.files; // => ["fileA.ts", "fileB.ts"], typed as string[]
  },
});
  • Would you be willing to help implement this feature?

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions