Skip to content

Commit

Permalink
fix: flag types (#454)
Browse files Browse the repository at this point in the history
* fix: flag types

* fix: support global flag types

* chore: remove comment

* fix: flag types when no global flags defined

* chore: rename computed prop
  • Loading branch information
mdonnalley committed Aug 5, 2022
1 parent e3ea8e1 commit 2938ba4
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 22 deletions.
16 changes: 8 additions & 8 deletions src/command.ts
Expand Up @@ -128,25 +128,25 @@ export default abstract class Command {
return cmd._run(argv)
}

protected static _globalFlags: Interfaces.FlagInput<any>
protected static _globalFlags: Interfaces.FlagInput

static get globalFlags(): Interfaces.FlagInput<any> {
static get globalFlags(): Interfaces.FlagInput {
return this._globalFlags
}

static set globalFlags(flags: Interfaces.FlagInput<any>) {
static set globalFlags(flags: Interfaces.FlagInput) {
this._globalFlags = Object.assign({}, this.globalFlags, flags)
this.flags = {} // force the flags setter to run
}

/** A hash of flags for the command */
protected static _flags: Interfaces.FlagInput<any>
protected static _flags: Interfaces.FlagInput

static get flags(): Interfaces.FlagInput<any> {
static get flags(): Interfaces.FlagInput {
return this._flags
}

static set flags(flags: Interfaces.FlagInput<any>) {
static set flags(flags: Interfaces.FlagInput) {
this._flags = Object.assign({}, this._flags ?? {}, this.globalFlags, flags)
}

Expand Down Expand Up @@ -242,11 +242,11 @@ export default abstract class Command {
g['http-call']!.userAgent = this.config.userAgent
}

protected async parse<F, A extends { [name: string]: any }>(options?: Interfaces.Input<F>, argv = this.argv): Promise<Interfaces.ParserOutput<F, A>> {
protected async parse<F, G, A extends { [name: string]: any }>(options?: Interfaces.Input<F, G>, argv = this.argv): Promise<Interfaces.ParserOutput<F, G, A>> {
if (!options) options = this.constructor as any
const opts = {context: this, ...options}
// the spread operator doesn't work with getters so we have to manually add it here
opts.flags = Object.assign({}, options?.flags, options?.globalFlags)
opts.flags = options?.flags
return Parser.parse(argv, opts)
}

Expand Down
13 changes: 7 additions & 6 deletions src/interfaces/parser.ts
Expand Up @@ -43,17 +43,18 @@ export type ArgInput = Arg<any>[]
export interface CLIParseErrorOptions {
parse: {
input?: ParserInput;
output?: ParserOutput<any, any>;
output?: ParserOutput;
};
}

export type OutputArgs = { [name: string]: any }
export type OutputFlags<T extends ParserInput['flags']> = { [P in keyof T]: any }
export type ParserOutput<TFlags extends OutputFlags<any>, TArgs extends OutputArgs> = {

export type ParserOutput<TFlags extends OutputFlags<any> = any, GFlags extends OutputFlags<any> = any, TArgs extends OutputArgs = any> = {
// Add in global flags so that they show up in the types
// This is necessary because there's no easy way to optionally return
// the individual flags based on wether they're enabled or not
flags: TFlags & { json: boolean | undefined };
flags: TFlags & GFlags & { json: boolean | undefined };
args: TArgs;
argv: string[];
raw: ParsingToken[];
Expand Down Expand Up @@ -162,9 +163,9 @@ export type EnumFlagOptions<T> = Partial<OptionFlag<T>> & {

export type Flag<T> = BooleanFlag<T> | OptionFlag<T>

export type Input<TFlags extends FlagOutput> = {
export type Input<TFlags extends FlagOutput, GFlags extends FlagOutput> = {
flags?: FlagInput<TFlags>;
globalFlags?: FlagInput<TFlags>;
globalFlags?: FlagInput<GFlags>;
args?: ArgInput;
strict?: boolean;
context?: any;
Expand Down Expand Up @@ -200,4 +201,4 @@ export type CompletableOptionFlag<T> = OptionFlag<T> & {

export type CompletableFlag<T> = BooleanFlag<T> | CompletableOptionFlag<T>

export type FlagInput<T extends FlagOutput> = { [P in keyof T]: CompletableFlag<T[P]> }
export type FlagInput<T extends FlagOutput = {[flag: string]: unknown}> = { [P in keyof T]: CompletableFlag<T[P]> }
12 changes: 5 additions & 7 deletions src/parser/index.ts
@@ -1,10 +1,8 @@
// tslint:disable interface-over-type-literal

import * as args from './args'
import Deps from './deps'
import * as flags from './flags'
import {Parser} from './parse'
import {Input, ParserOutput} from '../interfaces'
import {FlagInput, Input, ParserOutput} from '../interfaces'
import * as Validate from './validate'
export {args}
export {flags}
Expand All @@ -15,22 +13,22 @@ const m = Deps()
// eslint-disable-next-line node/no-missing-require
.add('validate', () => require('./validate').validate as typeof Validate.validate)

export async function parse<TFlags, TArgs extends { [name: string]: string }>(argv: string[], options: Input<TFlags>): Promise<ParserOutput<TFlags, TArgs>> {
export async function parse<TFlags, GFlags, TArgs extends { [name: string]: string }>(argv: string[], options: Input<TFlags, GFlags>): Promise<ParserOutput<TFlags, GFlags, TArgs>> {
const input = {
argv,
context: options.context,
args: (options.args || []).map((a: any) => args.newArg(a as any)),
'--': options['--'],
flags: {
color: flags.defaultFlags.color,
...((options.flags || {})) as any,
},
...options.flags,
} as FlagInput<any>,
strict: options.strict !== false,
}
const parser = new Parser(input)
const output = await parser.parse()
m.validate({input, output})
return output as ParserOutput<TFlags, TArgs>
return output as ParserOutput<TFlags, GFlags, TArgs>
}

const {boolean, integer, url, directory, file} = flags
Expand Down
2 changes: 1 addition & 1 deletion src/parser/validate.ts
Expand Up @@ -10,7 +10,7 @@ import {ParserArg, ParserInput, ParserOutput, Flag} from '../interfaces'

export function validate(parse: {
input: ParserInput;
output: ParserOutput<any, any>;
output: ParserOutput;
}) {
function validateArgs() {
const maxArgs = parse.input.args.length
Expand Down

0 comments on commit 2938ba4

Please sign in to comment.