Repository: oclif/core
Pull request #46 (2020-09-16 20:09:00)

feat: export parser interfaces in Interfaces

codecov[bot]: # [Codecov](https://codecov.io/gh/oclif/core/pull/46?src=pr&el=h1) Report
> Merging [#46](https://codecov.io/gh/oclif/core/pull/46?src=pr&el=desc) into [master](https://codecov.io/gh/oclif/core/commit/5d4212f6a3f8f56c66b6eeb4680da0ad0c944325?el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/oclif/core/pull/46/graphs/tree.svg?width=650&height=150&src=pr&token=K0kGk9tefA)](https://codecov.io/gh/oclif/core/pull/46?src=pr&el=tree)

```diff
@@          Coverage Diff           @@
##           master     #46   +/-   ##
======================================
  Coverage    0.00%   0.00%           
======================================
  Files           3       3           
  Lines          15      15           
  Branches        2       2           
======================================
  Misses         15      15           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/oclif/core/pull/46?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/oclif/core/pull/46?src=pr&el=footer). Last update [5d4212f...8f999d0](https://codecov.io/gh/oclif/core/pull/46?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/command.ts b/src/command.ts
index 01a1b08..4377c1d 100644
--- a/src/command.ts
+++ b/src/command.ts
@@ -6,7 +6,6 @@ import * as Errors from './errors'
 import {PrettyPrintableError} from './errors'
 import * as Parser from './parser'
 import {HelpBase, getHelpClass} from './help'
-import * as flags from './flags'
 import {sortBy, uniqBy} from './util'
 
 const pjson = require('../package.json')
@@ -58,10 +57,10 @@ export default abstract class Command {
   static parse = true
 
   /** A hash of flags for the command */
-  static flags?: flags.Input<any>
+  static flags?: Interfaces.FlagInput<any>
 
   /** An order-dependent array of arguments for the command */
-  static args?: Parser.args.Input
+  static args?: Interfaces.ArgInput
 
   static plugin: Interfaces.Plugin | undefined
 
@@ -158,7 +157,7 @@ export default abstract class Command {
     if (this._helpOverride()) return this._help()
   }
 
-  protected parse<F, A extends {[name: string]: any}>(options?: Parser.Input<F>, argv = this.argv): Parser.Output<F, A> {
+  protected parse<F, A extends { [name: string]: any }>(options?: Interfaces.Input<F>, argv = this.argv): Interfaces.ParserOutput<F, A> {
     if (!options) options = this.constructor as any
     return Parser.parse(argv, {context: this, ...options})
   }
diff --git a/src/flags.ts b/src/flags.ts
index ee4f331..7afca72 100644
--- a/src/flags.ts
+++ b/src/flags.ts
@@ -1,47 +1,18 @@
-import {Config} from './interfaces/config'
+import {OptionFlag, Definition, BooleanFlag, EnumFlagOptions} from './interfaces'
 import * as Parser from './parser'
 import Command from './command'
 
-export type ICompletionContext = {
-  args?: { [name: string]: string };
-  flags?: { [name: string]: string };
-  argv?: string[];
-  config: Config;
-}
-
-export type ICompletion = {
-  skipCache?: boolean;
-  cacheDuration?: number;
-  cacheKey?(ctx: ICompletionContext): Promise<string>;
-  options(ctx: ICompletionContext): Promise<string[]>;
-}
-
-export type IOptionFlag<T> = Parser.flags.IOptionFlag<T> & {
-  completion?: ICompletion;
-}
-
-export type IFlag<T> = Parser.flags.IBooleanFlag<T> | IOptionFlag<T>
-
-export type Output = Parser.flags.Output
-export type Input<T extends Output> = { [P in keyof T]: IFlag<T[P]> }
-
-export type Definition<T> = {
-  (options: {multiple: true} & Partial<IOptionFlag<T[]>>): IOptionFlag<T[]>;
-  (options: ({required: true} | {default: Parser.flags.Default<T>}) & Partial<IOptionFlag<T>>): IOptionFlag<T>;
-  (options?: Partial<IOptionFlag<T>>): IOptionFlag<T | undefined>;
-}
-
-export function build<T>(defaults: {parse: IOptionFlag<T>['parse']} & Partial<IOptionFlag<T>>): Definition<T>
-export function build(defaults: Partial<IOptionFlag<string>>): Definition<string>
-export function build<T>(defaults: Partial<IOptionFlag<T>>): Definition<T> {
+export function build<T>(defaults: {parse: OptionFlag<T>['parse']} & Partial<OptionFlag<T>>): Definition<T>
+export function build(defaults: Partial<OptionFlag<string>>): Definition<string>
+export function build<T>(defaults: Partial<OptionFlag<T>>): Definition<T> {
   return Parser.flags.build<T>(defaults as any)
 }
 
-export function option<T>(options: {parse: IOptionFlag<T>['parse']} & Partial<IOptionFlag<T>>) {
+export function option<T>(options: {parse: OptionFlag<T>['parse']} & Partial<OptionFlag<T>>) {
   return build<T>(options)()
 }
 
-const _enum = <T = string>(opts: Parser.flags.EnumFlagOptions<T>): IOptionFlag<T> => {
+const _enum = <T = string>(opts: EnumFlagOptions<T>): OptionFlag<T> => {
   return build<T>({
     parse(input) {
       if (!opts.options.includes(input)) throw new Error(`Expected --${this.name}=${input} to be one of: ${opts.options.join(', ')}`)
@@ -49,7 +20,7 @@ const _enum = <T = string>(opts: Parser.flags.EnumFlagOptions<T>): IOptionFlag<T
     },
     helpValue: `(${opts.options.join('|')})`,
     ...opts,
-  })() as IOptionFlag<T>
+  })() as OptionFlag<T>
 }
 export {_enum as enum}
 
@@ -57,7 +28,7 @@ const stringFlag = build({})
 export {stringFlag as string}
 export {boolean, integer} from './parser'
 
-export const version = (opts: Partial<Parser.flags.IBooleanFlag<boolean>> = {}) => {
+export const version = (opts: Partial<BooleanFlag<boolean>> = {}) => {
   return Parser.flags.boolean({
     // char: 'v',
     description: 'show CLI version',
@@ -69,7 +40,7 @@ export const version = (opts: Partial<Parser.flags.IBooleanFlag<boolean>> = {})
   })
 }
 
-export const help = (opts: Partial<Parser.flags.IBooleanFlag<boolean>> = {}) => {
+export const help = (opts: Partial<BooleanFlag<boolean>> = {}) => {
   return Parser.flags.boolean({
     // char: 'h',
     description: 'show CLI help',
diff --git a/src/parser/alphabet.ts b/src/interfaces/alphabet.ts
similarity index 100%
rename from src/parser/alphabet.ts
rename to src/interfaces/alphabet.ts
diff --git a/src/interfaces/command.ts b/src/interfaces/command.ts
index 58e05bf..a05213d 100644
--- a/src/interfaces/command.ts
+++ b/src/interfaces/command.ts
@@ -1,7 +1,6 @@
-import * as Parser from '../parser'
-
 import {Config, LoadOptions} from './config'
 import {Plugin} from './plugin'
+import {ArgInput, FlagInput} from './parser'
 
 export interface Command {
   id: string;
@@ -66,8 +65,8 @@ export namespace Command {
 
   export interface Class extends Base {
     plugin?: Plugin;
-    flags?: Parser.flags.Input<any>;
-    args?: Parser.args.Input;
+    flags?: FlagInput<any>;
+    args?: ArgInput;
     new(argv: string[], config: Config): Instance;
     run(argv?: string[], config?: LoadOptions): PromiseLike<any>;
   }
diff --git a/src/interfaces/flags.ts b/src/interfaces/flags.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index 44760f3..cff89b4 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -1,9 +1,19 @@
+export {AlphabetLowercase, AlphabetUppercase} from './alphabet'
 export {Config, ArchTypes, PlatformTypes, LoadOptions} from './config'
 export {Command} from './command'
 export {OclifError, PrettyPrintableError} from './errors'
 export {HelpOptions} from './help'
 export {Hook, HookKeyOrOptions, Hooks} from './hooks'
 export {Manifest} from './manifest'
+export {
+  ParserArg, Arg, ParseFn, ParserOutput, ParserInput, ArgToken,
+  OptionalArg, FlagOutput, OutputArgs, OutputFlags, FlagUsageOptions,
+  CLIParseErrorOptions, ArgInput, RequiredArg, Metadata, ParsingToken,
+  FlagToken, List, ListItem, BooleanFlag, Flag, FlagBase, OptionFlag,
+  Input, EnumFlagOptions, DefaultContext, Default, Definition,
+  CompletableOptionFlag, Completion, CompletionContext, FlagInput,
+  CompletableFlag,
+} from './parser'
 export {PJSON} from './pjson'
 export {Plugin, PluginOptions, Options} from './plugin'
 export {Topic} from './topic'
diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts
new file mode 100644
index 0000000..b65bf26
--- /dev/null
+++ b/src/interfaces/parser.ts
@@ -0,0 +1,169 @@
+import {AlphabetLowercase, AlphabetUppercase} from './alphabet'
+import {Config} from './config'
+
+export type ParseFn<T> = (input: string) => T
+
+export interface Arg<T = string> {
+  name: string;
+  description?: string;
+  required?: boolean;
+  hidden?: boolean;
+  parse?: ParseFn<T>;
+  default?: T | (() => T);
+  options?: string[];
+}
+
+export interface ArgBase<T> {
+  name?: string;
+  description?: string;
+  hidden?: boolean;
+  parse: ParseFn<T>;
+  default?: T | (() => T);
+  input?: string;
+  options?: string[];
+}
+
+export type RequiredArg<T> = ArgBase<T> & {
+  required: true;
+  value: T;
+}
+
+export type OptionalArg<T> = ArgBase<T> & {
+  required: false;
+  value?: T;
+}
+
+export type ParserArg<T> = RequiredArg<T> | OptionalArg<T>
+
+export interface FlagOutput { [name: string]: any }
+export type ArgInput = Arg<any>[]
+
+export interface CLIParseErrorOptions {
+  parse: {
+    input?: ParserInput;
+    output?: ParserOutput<any, any>;
+  };
+}
+
+export type OutputArgs<T extends ParserInput['args']> = { [P in keyof T]: any }
+export type OutputFlags<T extends ParserInput['flags']> = { [P in keyof T]: any }
+export type ParserOutput<TFlags extends OutputFlags<any>, TArgs extends OutputArgs<any>> = {
+  flags: TFlags;
+  args: TArgs;
+  argv: string[];
+  raw: ParsingToken[];
+  metadata: Metadata;
+}
+
+export type ArgToken = { type: 'arg'; input: string }
+export type FlagToken = { type: 'flag'; flag: string; input: string }
+export type ParsingToken = ArgToken | FlagToken
+
+export interface FlagUsageOptions { displayRequired?: boolean }
+
+export type Metadata = {
+  flags: { [key: string]: MetadataFlag };
+}
+
+type MetadataFlag = {
+  setFromDefault?: boolean;
+}
+
+export type ListItem = [string, string | undefined]
+export type List = ListItem[]
+
+export type DefaultContext<T> = {
+  options: OptionFlag<T>;
+  flags: { [k: string]: string };
+}
+
+export type Default<T> = T | ((context: DefaultContext<T>) => T)
+
+export type FlagBase<T, I> = {
+  name: string;
+  char?: AlphabetLowercase | AlphabetUppercase;
+  description?: string;
+  helpLabel?: string;
+  hidden?: boolean;
+  required?: boolean;
+  dependsOn?: string[];
+  exclusive?: string[];
+  exactlyOne?: string[];
+  /**
+   * also accept an environment variable as input
+   */
+  env?: string;
+  parse(input: I, context: any): T;
+}
+
+export type BooleanFlag<T> = FlagBase<T, boolean> & {
+  type: 'boolean';
+  allowNo: boolean;
+  /**
+   * specifying a default of false is the same not specifying a default
+   */
+  default?: Default<boolean>;
+}
+
+export type OptionFlag<T> = FlagBase<T, string> & {
+  type: 'option';
+  helpValue?: string;
+  default?: Default<T | undefined>;
+  multiple: boolean;
+  input: string[];
+  options?: string[];
+}
+
+export type Definition<T> = {
+  (options: { multiple: true } & Partial<OptionFlag<T[]>>): OptionFlag<T[]>;
+  (
+    options: ({ required: true } | { default: Default<T> }) &
+      Partial<OptionFlag<T>>,
+  ): OptionFlag<T>;
+  (options?: Partial<OptionFlag<T>>): OptionFlag<T | undefined>;
+}
+
+export type EnumFlagOptions<T> = Partial<OptionFlag<T>> & {
+  options: T[];
+}
+
+export type Flag<T> = BooleanFlag<T> | OptionFlag<T>
+
+export type Input<TFlags extends FlagOutput> = {
+  flags?: FlagInput<TFlags>;
+  args?: ArgInput;
+  strict?: boolean;
+  context?: any;
+  '--'?: boolean;
+}
+
+export interface ParserInput {
+  argv: string[];
+  flags: FlagInput<any>;
+  args: ParserArg<any>[];
+  strict: boolean;
+  context: any;
+  '--'?: boolean;
+}
+
+export type CompletionContext = {
+  args?: { [name: string]: string };
+  flags?: { [name: string]: string };
+  argv?: string[];
+  config: Config;
+}
+
+export type Completion = {
+  skipCache?: boolean;
+  cacheDuration?: number;
+  cacheKey?(ctx: CompletionContext): Promise<string>;
+  options(ctx: CompletionContext): Promise<string[]>;
+}
+
+export type CompletableOptionFlag<T> = OptionFlag<T> & {
+  completion?: Completion;
+}
+
+export type CompletableFlag<T> = BooleanFlag<T> | CompletableOptionFlag<T>
+
+export type FlagInput<T extends FlagOutput> = { [P in keyof T]: CompletableFlag<T[P]> }
diff --git a/src/parser/args.ts b/src/parser/args.ts
index f992da5..96d85a2 100644
--- a/src/parser/args.ts
+++ b/src/parser/args.ts
@@ -1,47 +1,11 @@
-export type ParseFn<T> = (input: string) => T
+import {ParserArg, Arg, ParseFn} from '../interfaces'
 
-// eslint-disable-next-line @typescript-eslint/interface-name-prefix
-export interface IArg<T = string> {
-  name: string;
-  description?: string;
-  required?: boolean;
-  hidden?: boolean;
-  parse?: ParseFn<T>;
-  default?: T | (() => T);
-  options?: string[];
-}
-
-export interface ArgBase<T> {
-  name?: string;
-  description?: string;
-  hidden?: boolean;
-  parse: ParseFn<T>;
-  default?: T | (() => T);
-  input?: string;
-  options?: string[];
-}
-
-export type RequiredArg<T> = ArgBase<T> & {
-  required: true;
-  value: T;
-}
-
-export type OptionalArg<T> = ArgBase<T> & {
-  required: false;
-  value?: T;
-}
-
-export type Arg<T> = RequiredArg<T> | OptionalArg<T>
-
-export function newArg<T>(arg: IArg<T> & { Parse: ParseFn<T> }): Arg<T>
-export function newArg(arg: IArg): Arg<string>
-export function newArg(arg: IArg<any>): any {
+export function newArg<T>(arg: Arg & { Parse: ParseFn<T> }): ParserArg<T>
+export function newArg(arg: Arg): ParserArg<string>
+export function newArg(arg: Arg<any>): any {
   return {
     parse: (i: string) => i,
     ...arg,
     required: Boolean(arg.required),
   }
 }
-
-export interface Output {[name: string]: any}
-export type Input = IArg<any>[]
diff --git a/src/parser/errors.ts b/src/parser/errors.ts
index 7a76279..61f94d4 100644
--- a/src/parser/errors.ts
+++ b/src/parser/errors.ts
@@ -1,11 +1,9 @@
 import {CLIError} from '../errors'
 
-import {Arg} from './args'
 import Deps from './deps'
-import * as flags from './flags'
 import * as Help from './help'
 import * as List from './list'
-import {ParserInput, ParserOutput} from './parse'
+import {ParserArg, CLIParseErrorOptions, OptionFlag, Flag} from '../interfaces'
 
 export {CLIError} from '../errors'
 
@@ -16,18 +14,10 @@ const m = Deps()
 // eslint-disable-next-line node/no-missing-require
 .add('list', () => require('./list') as typeof List)
 
-// eslint-disable-next-line @typescript-eslint/interface-name-prefix
-export interface ICLIParseErrorOptions {
-  parse: {
-    input?: ParserInput;
-    output?: ParserOutput<any, any>;
-  };
-}
-
 export class CLIParseError extends CLIError {
-  public parse: ICLIParseErrorOptions['parse']
+  public parse: CLIParseErrorOptions['parse']
 
-  constructor(options: ICLIParseErrorOptions & { message: string }) {
+  constructor(options: CLIParseErrorOptions & { message: string }) {
     options.message += '\nSee more help with --help'
     super(options.message)
     this.parse = options.parse
@@ -35,9 +25,9 @@ export class CLIParseError extends CLIError {
 }
 
 export class InvalidArgsSpecError extends CLIParseError {
-  public args: Arg<any>[]
+  public args: ParserArg<any>[]
 
-  constructor({args, parse}: ICLIParseErrorOptions & { args: Arg<any>[] }) {
+  constructor({args, parse}: CLIParseErrorOptions & { args: ParserArg<any>[] }) {
     let message = 'Invalid argument spec'
     const namedArgs = args.filter(a => a.name)
     if (namedArgs.length > 0) {
@@ -50,9 +40,9 @@ export class InvalidArgsSpecError extends CLIParseError {
 }
 
 export class RequiredArgsError extends CLIParseError {
-  public args: Arg<any>[]
+  public args: ParserArg<any>[]
 
-  constructor({args, parse}: ICLIParseErrorOptions & { args: Arg<any>[] }) {
+  constructor({args, parse}: CLIParseErrorOptions & { args: ParserArg<any>[] }) {
     let message = `Missing ${args.length} required arg${args.length === 1 ? '' : 's'}`
     const namedArgs = args.filter(a => a.name)
     if (namedArgs.length > 0) {
@@ -65,9 +55,9 @@ export class RequiredArgsError extends CLIParseError {
 }
 
 export class RequiredFlagError extends CLIParseError {
-  public flag: flags.IFlag<any>
+  public flag: Flag<any>
 
-  constructor({flag, parse}: ICLIParseErrorOptions & { flag: flags.IFlag<any> }) {
+  constructor({flag, parse}: CLIParseErrorOptions & { flag: Flag<any> }) {
     const usage = m.list.renderList(m.help.flagUsages([flag], {displayRequired: false}))
     const message = `Missing required flag:\n${usage}`
     super({parse, message})
@@ -78,7 +68,7 @@ export class RequiredFlagError extends CLIParseError {
 export class UnexpectedArgsError extends CLIParseError {
   public args: string[]
 
-  constructor({parse, args}: ICLIParseErrorOptions & { args: string[] }) {
+  constructor({parse, args}: CLIParseErrorOptions & { args: string[] }) {
     const message = `Unexpected argument${args.length === 1 ? '' : 's'}: ${args.join(', ')}`
     super({parse, message})
     this.args = args
@@ -86,14 +76,14 @@ export class UnexpectedArgsError extends CLIParseError {
 }
 
 export class FlagInvalidOptionError extends CLIParseError {
-  constructor(flag: flags.IOptionFlag<any>, input: string) {
+  constructor(flag: OptionFlag<any>, input: string) {
     const message = `Expected --${flag.name}=${input} to be one of: ${flag.options!.join(', ')}`
     super({parse: {}, message})
   }
 }
 
 export class ArgInvalidOptionError extends CLIParseError {
-  constructor(arg: Arg<any>, input: string) {
+  constructor(arg: ParserArg<any>, input: string) {
     const message = `Expected ${input} to be one of: ${arg.options!.join(', ')}`
     super({parse: {}, message})
   }
diff --git a/src/parser/flags.ts b/src/parser/flags.ts
index e27f532..cac0f0b 100644
--- a/src/parser/flags.ts
+++ b/src/parser/flags.ts
@@ -1,71 +1,14 @@
 // tslint:disable interface-over-type-literal
 
-import {AlphabetLowercase, AlphabetUppercase} from './alphabet'
-
-export type DefaultContext<T> = {
-  options: IOptionFlag<T>;
-  flags: {[k: string]: string};
-}
-
-export type Default<T> = T | ((context: DefaultContext<T>) => T)
-
-export type IFlagBase<T, I> = {
-  name: string;
-  char?: AlphabetLowercase | AlphabetUppercase;
-  description?: string;
-  helpLabel?: string;
-  hidden?: boolean;
-  required?: boolean;
-  dependsOn?: string[];
-  exclusive?: string[];
-  exactlyOne?: string[];
-  /**
-   * also accept an environment variable as input
-   */
-  env?: string;
-  parse(input: I, context: any): T;
-}
-
-export type IBooleanFlag<T> = IFlagBase<T, boolean> & {
-  type: 'boolean';
-  allowNo: boolean;
-  /**
-   * specifying a default of false is the same not specifying a default
-   */
-  default?: Default<boolean>;
-}
-
-export type IOptionFlag<T> = IFlagBase<T, string> & {
-  type: 'option';
-  helpValue?: string;
-  default?: Default<T | undefined>;
-  multiple: boolean;
-  input: string[];
-  options?: string[];
-}
-
-export type Definition<T> = {
-  (options: {multiple: true} & Partial<IOptionFlag<T[]>>): IOptionFlag<T[]>;
-  (
-    options: ({required: true} | {default: Default<T>}) &
-      Partial<IOptionFlag<T>>,
-  ): IOptionFlag<T>;
-  (options?: Partial<IOptionFlag<T>>): IOptionFlag<T | undefined>;
-}
-
-export type EnumFlagOptions<T> = Partial<IOptionFlag<T>> & {
-  options: T[];
-}
-
-export type IFlag<T> = IBooleanFlag<T> | IOptionFlag<T>
+import {Definition, OptionFlag, BooleanFlag} from '../interfaces'
 
 export function build<T>(
-  defaults: {parse: IOptionFlag<T>['parse']} & Partial<IOptionFlag<T>>,
+  defaults: {parse: OptionFlag<T>['parse']} & Partial<OptionFlag<T>>,
 ): Definition<T>
 export function build(
-  defaults: Partial<IOptionFlag<string>>,
+  defaults: Partial<OptionFlag<string>>,
 ): Definition<string>
-export function build<T>(defaults: Partial<IOptionFlag<T>>): Definition<T> {
+export function build<T>(defaults: Partial<OptionFlag<T>>): Definition<T> {
   return (options: any = {}): any => {
     return {
       parse: (i: string, _: any) => i,
@@ -79,14 +22,14 @@ export function build<T>(defaults: Partial<IOptionFlag<T>>): Definition<T> {
 }
 
 export function boolean<T = boolean>(
-  options: Partial<IBooleanFlag<T>> = {},
-): IBooleanFlag<T> {
+  options: Partial<BooleanFlag<T>> = {},
+): BooleanFlag<T> {
   return {
     parse: (b, _) => b,
     ...options,
     allowNo: Boolean(options.allowNo),
     type: 'boolean',
-  } as IBooleanFlag<T>
+  } as BooleanFlag<T>
 }
 
 export const integer = build({
@@ -98,7 +41,7 @@ export const integer = build({
 })
 
 export function option<T>(
-  options: {parse: IOptionFlag<T>['parse']} & Partial<IOptionFlag<T>>,
+  options: {parse: OptionFlag<T>['parse']} & Partial<OptionFlag<T>>,
 ) {
   return build<T>(options)()
 }
@@ -109,6 +52,3 @@ export {stringFlag as string}
 export const defaultFlags = {
   color: boolean({allowNo: true}),
 }
-
-export type Output = {[name: string]: any}
-export type Input<T extends Output> = {[P in keyof T]: IFlag<T[P]>}
diff --git a/src/parser/help.ts b/src/parser/help.ts
index 744067b..a00f301 100644
--- a/src/parser/help.ts
+++ b/src/parser/help.ts
@@ -1,8 +1,8 @@
 import * as Chalk from 'chalk'
 
 import Deps from './deps'
-import {IFlag} from './flags'
 import * as Util from './util'
+import {FlagUsageOptions, Flag} from '../interfaces'
 
 // eslint-disable-next-line new-cap
 const m = Deps()
@@ -10,8 +10,7 @@ const m = Deps()
 // eslint-disable-next-line node/no-missing-require
 .add('util', () => require('./util') as typeof Util)
 
-export interface FlagUsageOptions { displayRequired?: boolean }
-export function flagUsage(flag: IFlag<any>, options: FlagUsageOptions = {}): [string, string | undefined] {
+export function flagUsage(flag: Flag<any>, options: FlagUsageOptions = {}): [string, string | undefined] {
   const label = []
 
   if (flag.helpLabel) {
@@ -30,7 +29,7 @@ export function flagUsage(flag: IFlag<any>, options: FlagUsageOptions = {}): [st
   return [` ${label.join(',').trim()}${usage}`, description] as [string, string | undefined]
 }
 
-export function flagUsages(flags: IFlag<any>[], options: FlagUsageOptions = {}): [string, string | undefined][] {
+export function flagUsages(flags: Flag<any>[], options: FlagUsageOptions = {}): [string, string | undefined][] {
   if (flags.length === 0) return []
   const {sortBy} = m.util
   return sortBy(flags, f => [f.char ? -1 : 1, f.char, f.name])
diff --git a/src/parser/index.ts b/src/parser/index.ts
index 7d38373..ea05449 100644
--- a/src/parser/index.ts
+++ b/src/parser/index.ts
@@ -3,7 +3,8 @@
 import * as args from './args'
 import Deps from './deps'
 import * as flags from './flags'
-import {OutputArgs, OutputFlags, Parser, ParserOutput as Output} from './parse'
+import {Parser} from './parse'
+import {Input, ParserOutput} from '../interfaces'
 import * as Validate from './validate'
 export {args}
 export {flags}
@@ -14,15 +15,7 @@ const m = Deps()
 // eslint-disable-next-line node/no-missing-require
 .add('validate', () => require('./validate').validate as typeof Validate.validate)
 
-export type Input<TFlags extends flags.Output> = {
-  flags?: flags.Input<TFlags>;
-  args?: args.Input;
-  strict?: boolean;
-  context?: any;
-  '--'?: boolean;
-}
-
-export function parse<TFlags, TArgs extends {[name: string]: string}>(argv: string[], options: Input<TFlags>): Output<TFlags, TArgs> {
+export function parse<TFlags, TArgs extends { [name: string]: string }>(argv: string[], options: Input<TFlags>): ParserOutput<TFlags, TArgs> {
   const input = {
     argv,
     context: options.context,
@@ -42,4 +35,4 @@ export function parse<TFlags, TArgs extends {[name: string]: string}>(argv: stri
 
 const boolean = flags.boolean
 const integer = flags.integer
-export {OutputFlags, OutputArgs, Output, boolean, integer}
+export {boolean, integer}
diff --git a/src/parser/list.ts b/src/parser/list.ts
index 8de18c0..2d31e91 100644
--- a/src/parser/list.ts
+++ b/src/parser/list.ts
@@ -1,5 +1,6 @@
 import {stdtermwidth} from './screen'
 import {maxBy} from './util'
+import {List} from '../interfaces'
 
 function linewrap(length: number, s: string): string {
   const lw = require('@oclif/linewrap')
@@ -8,9 +9,7 @@ function linewrap(length: number, s: string): string {
   })(s).trim()
 }
 
-export type IListItem = [string, string | undefined]
-export type IList = IListItem[]
-export function renderList(items: IListItem[]): string {
+export function renderList(items: List): string {
   if (items.length === 0) {
     return ''
   }
diff --git a/src/parser/metadata.ts b/src/parser/metadata.ts
deleted file mode 100644
index 9b6c34d..0000000
--- a/src/parser/metadata.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type Metadata = {
-  flags: { [key: string]: MetadataFlag };
-}
-
-type MetadataFlag = {
-  setFromDefault?: boolean;
-}
diff --git a/src/parser/parse.ts b/src/parser/parse.ts
index a76b36f..be18ea8 100644
--- a/src/parser/parse.ts
+++ b/src/parser/parse.ts
@@ -1,11 +1,9 @@
 // tslint:disable interface-over-type-literal
 
-import {Arg} from './args'
 import Deps from './deps'
 import * as Errors from './errors'
-import * as Flags from './flags'
-import {Metadata} from './metadata'
 import * as Util from './util'
+import {ParserInput, OutputFlags, ParsingToken, OutputArgs, ArgToken, FlagToken, BooleanFlag, OptionFlag} from '../interfaces'
 
 // eslint-disable-next-line new-cap
 const m = Deps()
@@ -25,41 +23,18 @@ try {
   debug = () => {}
 }
 
-export type OutputArgs<T extends ParserInput['args']> = { [P in keyof T]: any }
-export type OutputFlags<T extends ParserInput['flags']> = { [P in keyof T]: any }
-export type ParserOutput<TFlags extends OutputFlags<any>, TArgs extends OutputArgs<any>> = {
-  flags: TFlags;
-  args: TArgs;
-  argv: string[];
-  raw: ParsingToken[];
-  metadata: Metadata;
-}
-
-export type ArgToken = { type: 'arg'; input: string }
-export type FlagToken = { type: 'flag'; flag: string; input: string }
-export type ParsingToken = ArgToken | FlagToken
-
-export interface ParserInput {
-  argv: string[];
-  flags: Flags.Input<any>;
-  args: Arg<any>[];
-  strict: boolean;
-  context: any;
-  '--'?: boolean;
-}
-
 export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']>, TArgs extends OutputArgs<T['args']>> {
   private readonly argv: string[]
 
   private readonly raw: ParsingToken[] = []
 
-  private readonly booleanFlags: { [k: string]: Flags.IBooleanFlag<any> }
+  private readonly booleanFlags: { [k: string]: BooleanFlag<any> }
 
   private readonly context: any
 
   private readonly metaData: any
 
-  private currentFlag?: Flags.IOptionFlag<any>
+  private currentFlag?: OptionFlag<any>
 
   constructor(private readonly input: T) {
     const {pickBy} = m.util
diff --git a/src/parser/util.ts b/src/parser/util.ts
index 2214de7..38da6c1 100644
--- a/src/parser/util.ts
+++ b/src/parser/util.ts
@@ -17,7 +17,7 @@ export function maxBy<T>(arr: T[], fn: (i: T) => number): T | undefined {
   return max && max.element
 }
 
-export type SortTypes = string | number | undefined | boolean
+type SortTypes = string | number | undefined | boolean
 
 export function sortBy<T>(arr: T[], fn: (i: T) => SortTypes | SortTypes[]): T[] {
   // function castType(t: SortTypes | SortTypes[]): string | number | SortTypes[] {
diff --git a/src/parser/validate.ts b/src/parser/validate.ts
index d17c098..4c703fa 100644
--- a/src/parser/validate.ts
+++ b/src/parser/validate.ts
@@ -1,14 +1,12 @@
 import {CLIError} from '../errors'
 
-import {Arg} from './args'
 import {
   InvalidArgsSpecError,
   RequiredArgsError,
   RequiredFlagError,
   UnexpectedArgsError,
 } from './errors'
-import {ParserInput, ParserOutput} from './parse'
-import {IFlag} from './flags'
+import {ParserArg, ParserInput, ParserOutput, Flag} from '../interfaces'
 
 export function validate(parse: {
   input: ParserInput;
@@ -21,7 +19,7 @@ export function validate(parse: {
       throw new UnexpectedArgsError({parse, args: extras})
     }
 
-    const missingRequiredArgs: Arg<any>[] = []
+    const missingRequiredArgs: ParserArg<any>[] = []
     let hasOptional = false
 
     parse.input.args.forEach((arg, index) => {
@@ -45,7 +43,7 @@ export function validate(parse: {
     }
   }
 
-  function validateAcrossFlags(flag: IFlag<any>) {
+  function validateAcrossFlags(flag: Flag<any>) {
     const intersection = Object.entries(parse.input.flags)
     .map(entry => entry[0]) // array of flag names
     .filter(flagName => parse.output.flags[flagName] !== undefined) // with values
diff --git a/src/util.ts b/src/util.ts
index 93c5f13..155c708 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -8,12 +8,11 @@ export function uniqBy<T>(arr: T[], fn: (cur: T) => any): T[] {
     return !arr.find((b, j) => j > i && fn(b) === aVal)
   })
 }
-export namespace sort {
-  export type Types = string | number | undefined | boolean
-}
 
-export function sortBy<T>(arr: T[], fn: (i: T) => sort.Types | sort.Types[]): T[] {
-  function compare(a: sort.Types | sort.Types[], b: sort.Types | sort.Types[]): number {
+type SortTypes = string | number | undefined | boolean
+
+export function sortBy<T>(arr: T[], fn: (i: T) => SortTypes | SortTypes[]): T[] {
+  function compare(a: SortTypes | SortTypes[], b: SortTypes | SortTypes[]): number {
     a = a === undefined ? 0 : a
     b = b === undefined ? 0 : b
 


Test patch:
diff --git a/test/parser/parse.test.ts b/test/parser/parse.test.ts
index bf40226..979ea05 100644
--- a/test/parser/parse.test.ts
+++ b/test/parser/parse.test.ts
@@ -2,6 +2,7 @@
 import {expect} from 'chai'
 
 import {flags, parse} from '../../src/parser'
+import {Interfaces} from '../../src'
 
 describe('parse', () => {
   it('--bool', () => {
@@ -484,7 +485,7 @@ See more help with --help`)
     })
 
     it('default has options', () => {
-      const def: flags.Default<string | undefined> = ({options}) =>
+      const def: Interfaces.Default<string | undefined> = ({options}) =>
         options.description
       const out = parse([], {
         // args: [{ name: 'baz', default: () => 'BAZ' }],
@@ -496,7 +497,7 @@ See more help with --help`)
     })
 
     it('can default to a different flag', () => {
-      const def: flags.Default<string | undefined> = opts => opts.flags.foo
+      const def: Interfaces.Default<string | undefined> = opts => opts.flags.foo
       const out = parse(['--foo=bar'], {
         flags: {
           bar: flags.string({