From 0ecc038d41b83d1118b3c9064157c305071093d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Viken=20Valv=C3=A5g?= Date: Mon, 21 Jan 2019 17:55:09 +0100 Subject: [PATCH] - Add default type parameters {} to all exported types for improved backwards compatibility. - Infer option types for positional options. - Add tests for command modules and command builders. --- types/yargs/index.d.ts | 13 ++++--- types/yargs/yargs-tests.ts | 78 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/types/yargs/index.d.ts b/types/yargs/index.d.ts index 005c05e436..d5a67c56b9 100644 --- a/types/yargs/index.d.ts +++ b/types/yargs/index.d.ts @@ -176,7 +176,8 @@ declare namespace yargs { * 'positional' should be called in a command's builder function, and is not * available on the top-level yargs instance. If so, it will throw an error. */ - positional(key: string, opt: PositionalOptions): Argv; + positional(key: K, opt: O): Argv & { [key in K]: InferredOptionType }>; + positional(key: K, opt: O): Argv }>; recommendCommands(): Argv; @@ -252,7 +253,7 @@ declare namespace yargs { wrap(columns: number | null): Argv; } - type Arguments = T & { + type Arguments = T & { /** Non-option arguments */ _: string[]; /** The script name or node command */ @@ -376,7 +377,7 @@ declare namespace yargs { type InferredOptionTypes = { [key in keyof O]: InferredOptionType }; - interface CommandModule { + interface CommandModule { aliases?: ReadonlyArray | string; builder?: CommandBuilder; command?: ReadonlyArray | string; @@ -384,11 +385,11 @@ declare namespace yargs { handler: (args: Arguments) => void; } - type ParseCallback = (err: Error | undefined, argv: Arguments, output: string) => void; - type CommandBuilder = { [key: string]: Options } | ((args: Argv) => Argv); + type ParseCallback = (err: Error | undefined, argv: Arguments, output: string) => void; + type CommandBuilder = { [key: string]: Options } | ((args: Argv) => Argv); type SyncCompletionFunction = (current: string, argv: any) => string[]; type AsyncCompletionFunction = (current: string, argv: any, done: (completion: ReadonlyArray) => void) => void; - type MiddlewareFunction = (args: Arguments) => void; + type MiddlewareFunction = (args: Arguments) => void; type Choices = ReadonlyArray; type PositionalOptionsType = "boolean" | "number" | "string"; } diff --git a/types/yargs/yargs-tests.ts b/types/yargs/yargs-tests.ts index 298ee9c9ef..605a986eaf 100644 --- a/types/yargs/yargs-tests.ts +++ b/types/yargs/yargs-tests.ts @@ -307,6 +307,84 @@ function Argv$command() { .argv; } +function Argv$commandModule() { + class CommandOne implements yargs.CommandModule { + handler(args: yargs.Arguments): void { + console.log("one"); + } + } + + const CommandTwo: yargs.CommandModule<{ a: string }, { b: number }> = { + builder: yargs => { + // $ExpectType: string + yargs.argv.a; + return yargs.number("b").default("b", parseInt(yargs.argv.a, 10)); + }, + + handler: argv => { + // $ExpectType: number + argv.b; + } + }; + + class Configure implements yargs.CommandModule<{ verbose: boolean }, { verbose: boolean, key: string, value: boolean }> { + command = 'configure [value]'; + aliases = ['config', 'cfg']; + describe = 'Set a config variable'; + + builder(yargs: yargs.Argv<{ verbose: boolean }>) { + return yargs.positional('key', { default: '' }).positional('value', { default: true }); + } + + handler(argv: yargs.Arguments<{ verbose: boolean, key: string, value: string | boolean }>) { + if (argv.verbose) { + console.log(`setting ${argv.key} to ${argv.value}`); + } + } + } + + const Configure2: yargs.CommandModule<{ verbose: boolean }, { verbose: boolean, key: string, value: boolean }> = { + command: 'configure [value]', + aliases: ['config', 'cfg'], + describe: 'Set a config variable', + + builder: yargs => { + return yargs.positional('key', { default: '' }).positional('value', { default: true }); + }, + + handler: argv => { + if (argv.verbose) { + console.log(`setting ${argv.key} to ${argv.value}`); + } + } + }; + + const command = 'import-file '; + const describe = 'run the importer on a single file'; + const builder = (yargs: yargs.Argv) => { + return yargs + .positional('file', { + describe: 'path to file to import' + }) + .options({ + cleanDestination: { + boolean: true, + describe: 'Clean the destination folder from previously generated files before proceeding.' + } + }); + }; + + const commandArgs = builder(yargs).argv; + + // $ExpectType: { [x: string]: unknown; file: unknown; cleanDestination: boolean | undefined; _: string[]; $0: string; } + commandArgs; + + // Backwards compatibility with older types + const builder2: yargs.CommandBuilder = builder; + const commandArgs2: yargs.Arguments = builder(yargs).argv; + const commandArgs3: yargs.Arguments = builder2(yargs).argv; +} + function Argv$completion_sync() { const argv = yargs .completion('completion', (current, argv) => {