EN

字段选项

定义命令标志、标量值、重复值、默认值、别名和校验。

更新于 2周前

字段选项既是命令属性,也定义了 CLI 对外提供的选项。func 会根据当前命令中的装饰字段创建 选项解析器,把解析值或默认值赋给实例,完成校验,然后才调用处理器。

字段选项只属于一个命令作用域。ServeCommand 声明的选项可用于 ship serve,不会自动出现在主命令或其他具名命令中。

选择合适的值类型

用户需求输入示例装饰器字段值
是或否的开关--verbose 或 -v@Flag()boolean
一个字符串、数字或布尔值--port 3000@Value()string | number | boolean
同一选项重复多次--include src --include tests@ArrayValue()string[]

布尔标志

@Flag() 创建布尔开关。用户没有传入该选项时,字段保留属性初始值;使用长名称或单字符 别名时,字段值为 true

src/commands/serve.command.ts
import { Command, Flag, Handler } from 'func'

@Command({ name: 'serve' })
export class ServeCommand {
  @Flag({ alias: 'v', description: 'Print request logs' })
  verbose = false

  @Handler()
  run() {
    console.log(this.verbose)
  }
}

ship serve --verboseship serve -v 都会得到 this.verbose === true 。字段标志用于向当前动作提供布尔值。如果该选项应该选择 另一个方法,请改用处理器标志,详见命令

标量值

@Value() 接收一个值。func 可以根据 TypeScript 发出的装饰器元数据推断 StringNumberBoolean 。可选属性或其他无法在运行时推断的声明需要显式传入 type

src/commands/serve.command.ts
import { Command, Handler, Value } from 'func'

@Command({ name: 'serve' })
export class ServeCommand {
  @Value({ description: 'Interface to bind' })
  host: string = 'localhost'

  @Value({ alias: 'p', description: 'Port to listen on' })
  port: number = 3000

  @Value({ name: 'config-file', type: String })
  configFile?: string

  @Handler()
  run() {
    console.log(this.host, this.port, this.configFile)
  }
}

ship serve --host 0.0.0.0 --port 4000 --config-file ./dev.json 会分别为三个字段赋予字符串、数字和字符串。属性名默认作为对外选项名。驼峰字段如果需要常见 的 kebab-case 时,应像 configFile 一样显式设置 name

用户省略选项时,属性初始值就是默认值。默认值会直接影响应用行为;如果用户需要知道 它,帮助信息中也应注明。

重复字符串值

@ArrayValue() 会把同一选项的多次输入收集为字符串数组。如果顺序或重复次数有意义,应使用该装饰器, 不要另外设计逗号分隔格式并在业务代码中解析。

src/commands/build.command.ts
import { ArrayValue, Command, Handler } from 'func'

@Command({ name: 'build' })
export class BuildCommand {
  @ArrayValue({ name: 'include', alias: 'i' })
  includes: string[] = []

  @Handler()
  run() {
    console.log(this.includes)
  }
}

ship build -i src -i tests 会把 ['src', 'tests'] 赋给 this.includes。ArrayValue 当前包含字符串;只有公开 CLI 确实需要其他表示时,可以在业务代码中转换。

在业务代码运行前完成校验

字段获得解析值或默认值后,func 会在处理器运行前执行校验器。校验失败会产生 runtime-print 输入错误,处理器不会运行。

src/commands/publish.command.ts
import { Command, DependsOn, Enum, Exclusive, Flag, Handler, Required, Value, ValueValidate } from 'func'

@Command({ name: 'publish' })
export class PublishCommand {
  @Required()
  @Enum(['dev', 'prod'])
  @Value({ type: String })
  target?: string

  @DependsOn(['token'])
  @Value({ type: String })
  registry?: string

  @Value({ type: String })
  token?: string

  @Exclusive(['json'])
  @Flag()
  table = false

  @Flag()
  json = false

  @ValueValidate((value) => Number(value) > 0 || 'retry must be positive')
  @Value()
  retry: number = 1

  @Handler()
  run() {}
}
  • @Required() 拒绝 undefined 。已定义的属性默认值会满足该规则,因此必须由用户提供的选项不要设置默认值。
  • @Enum(values) 只接受列表内的标量值;对于数组,则要求每一项都在列表内。
  • @DependsOn(['token']) 只在装饰的选项被显式传入时要求同时提供 --token
  • @Exclusive(['json']) 会拒绝两个选项都被显式传入的调用。
  • @ValueValidate(fn) 接收归一化值和所有选项值。返回 false 会产生通用错误,返回字符串会显示自定义信息,没有返回值表示校验通过。

依赖和互斥校验器接收的是不带 -- 的公开长选项名,不是 TypeScript 属性名或短 alias。

仅解析的子选项

@SubOptions() 声明只出现在 @Args().option 中的选项,func 不会把它们赋给字段。它适合兼容已有的动态选项设计,但无法直接在命令实例上看到可用数据和默认值。 普通的类型化命令应优先使用字段选项。

src/commands/inspect.command.ts
import { Args, Command, FuncArgs, Handler, SubOptions } from 'func'

@SubOptions([
  { name: 'format', alias: 'f', type: String },
  { name: 'raw', type: Boolean },
])
@Command({ name: 'inspect' })
export class InspectCommand {
  @Handler()
  run(@Args() args: FuncArgs) {
    console.log(args.option.format, args.option.raw)
  }
}

看起来像选项但没有声明的 token 会被拒绝。剩余位置输入和完整运行时上下文见 参数注入