From 6b8c6078f87ad97f7c11567bb6946df35a4a9846 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Sat, 11 May 2019 11:53:33 -0700 Subject: [PATCH 01/12] cli: accept csv for array values --- lighthouse-cli/cli-flags.js | 34 ++++++++++++++++++++--- lighthouse-cli/test/cli/cli-flags-test.js | 10 +++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 06e96f93dab2..4a7bc04fe953 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -12,12 +12,21 @@ const pkg = require('../package.json'); const printer = require('./printer'); /** - * @param {string=} manualArgv + * @param {(string | string[])[]} arr + * @return {string[]} + */ +function flatten(arr) { + /** @type {string[]} */ + const result = []; + return result.concat(...arr); +} + +/** + * @param {string[]} manualArgv * @return {LH.CliFlags} */ -function getFlags(manualArgv) { - // @ts-ignore yargs() is incorrectly typed as not returning itself - const y = manualArgv ? yargs(manualArgv) : yargs; +function getFlags(...manualArgv) { + const y = manualArgv.length ? yargs(manualArgv) : yargs; return y.help('help') .version(() => pkg.version) .showHelpOnFail(false, 'Specify --help for available options') @@ -152,6 +161,23 @@ function getFlags(manualArgv) { .default('enable-error-reporting', undefined) // Undefined so prompted by default .default('channel', 'cli') .check(/** @param {LH.CliFlags} argv */ (argv) => { + // ".middleware" does not exist in this version of yargs, so do some preprocessing here. + /** @type {(keyof LH.CliFlags)[]} */ + const arrayKeys = [ + 'blockedUrlPatterns', + 'onlyAudits', + 'onlyCategories', + 'skipAudits', + 'output', + 'plugins', + ]; + arrayKeys.forEach(key => { + const input = /** @type {string[]} */ (argv[key]); + if (input) { + argv[key] = flatten(input.map(value => value.split(','))); + } + }); + // Lighthouse doesn't need a URL if... // - We're just listing the available options. // - We're just printing the config. diff --git a/lighthouse-cli/test/cli/cli-flags-test.js b/lighthouse-cli/test/cli/cli-flags-test.js index 99e676e37551..284a10fb0f06 100644 --- a/lighthouse-cli/test/cli/cli-flags-test.js +++ b/lighthouse-cli/test/cli/cli-flags-test.js @@ -28,4 +28,14 @@ describe('CLI bin', function() { assert.ok(optionsWithDescriptions.includes(opt), `cli option '${opt}' has no description`); }); }); + + it('array values support csv', () => { + const flags = getFlags( + 'http://www.example.com', + '--only-categories=performance,seo', + '--skipAudits=unused-javascript,redirects', + '--skipAudits=bootup-time'); + expect(flags.onlyCategories).toEqual(['performance', 'seo']); + expect(flags.skipAudits).toEqual(['unused-javascript', 'redirects', 'bootup-time']); + }); }); From 9fe961a8343a859293847e9f8ee00b92d02f1953 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Sat, 11 May 2019 12:40:59 -0700 Subject: [PATCH 02/12] dont hardcode list of array keys --- lighthouse-cli/cli-flags.js | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 4a7bc04fe953..25bf5794102e 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -162,21 +162,15 @@ function getFlags(...manualArgv) { .default('channel', 'cli') .check(/** @param {LH.CliFlags} argv */ (argv) => { // ".middleware" does not exist in this version of yargs, so do some preprocessing here. - /** @type {(keyof LH.CliFlags)[]} */ - const arrayKeys = [ - 'blockedUrlPatterns', - 'onlyAudits', - 'onlyCategories', - 'skipAudits', - 'output', - 'plugins', - ]; - arrayKeys.forEach(key => { - const input = /** @type {string[]} */ (argv[key]); - if (input) { - argv[key] = flatten(input.map(value => value.split(','))); + for (const _key of Object.keys(argv)) { + const key = /** @type {keyof argv} */ (_key); + const input = argv[key]; + if (Array.isArray(input)) { + // These values are guarenteed to be strings. + const strings = /** @type {string[]} */ (input); + argv[key] = flatten(strings.map(value => value.split(','))); } - }); + } // Lighthouse doesn't need a URL if... // - We're just listing the available options. From 35e2f017fb4bea22e63fde81e05669caf7d6bca1 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 13 May 2019 11:51:47 -0700 Subject: [PATCH 03/12] Revert "dont hardcode list of array keys" This reverts commit 9fe961a8343a859293847e9f8ee00b92d02f1953. --- lighthouse-cli/cli-flags.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 25bf5794102e..4a7bc04fe953 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -162,15 +162,21 @@ function getFlags(...manualArgv) { .default('channel', 'cli') .check(/** @param {LH.CliFlags} argv */ (argv) => { // ".middleware" does not exist in this version of yargs, so do some preprocessing here. - for (const _key of Object.keys(argv)) { - const key = /** @type {keyof argv} */ (_key); - const input = argv[key]; - if (Array.isArray(input)) { - // These values are guarenteed to be strings. - const strings = /** @type {string[]} */ (input); - argv[key] = flatten(strings.map(value => value.split(','))); + /** @type {(keyof LH.CliFlags)[]} */ + const arrayKeys = [ + 'blockedUrlPatterns', + 'onlyAudits', + 'onlyCategories', + 'skipAudits', + 'output', + 'plugins', + ]; + arrayKeys.forEach(key => { + const input = /** @type {string[]} */ (argv[key]); + if (input) { + argv[key] = flatten(input.map(value => value.split(','))); } - } + }); // Lighthouse doesn't need a URL if... // - We're just listing the available options. From 879b2881847914a028e791cd1b4a9de91504ca25 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 13 May 2019 11:52:21 -0700 Subject: [PATCH 04/12] add tests for csv whitelist --- lighthouse-cli/cli-flags.js | 3 +-- lighthouse-cli/test/cli/cli-flags-test.js | 12 +++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 4a7bc04fe953..198dad55f398 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -164,12 +164,11 @@ function getFlags(...manualArgv) { // ".middleware" does not exist in this version of yargs, so do some preprocessing here. /** @type {(keyof LH.CliFlags)[]} */ const arrayKeys = [ - 'blockedUrlPatterns', 'onlyAudits', 'onlyCategories', - 'skipAudits', 'output', 'plugins', + 'skipAudits', ]; arrayKeys.forEach(key => { const input = /** @type {string[]} */ (argv[key]); diff --git a/lighthouse-cli/test/cli/cli-flags-test.js b/lighthouse-cli/test/cli/cli-flags-test.js index 284a10fb0f06..2990f4da5b0d 100644 --- a/lighthouse-cli/test/cli/cli-flags-test.js +++ b/lighthouse-cli/test/cli/cli-flags-test.js @@ -29,7 +29,7 @@ describe('CLI bin', function() { }); }); - it('array values support csv', () => { + it('array values support csv when appropriate', () => { const flags = getFlags( 'http://www.example.com', '--only-categories=performance,seo', @@ -38,4 +38,14 @@ describe('CLI bin', function() { expect(flags.onlyCategories).toEqual(['performance', 'seo']); expect(flags.skipAudits).toEqual(['unused-javascript', 'redirects', 'bootup-time']); }); + + it('array values do not support csv when appropriate', () => { + const flags = getFlags( + 'http://www.example.com', + '--chrome-flags="--window-size 800,600"', + '--chrome-flags="--enabled-features=NetworkService,VirtualTime"', + '--blockedUrlPatterns=.*x,y\\.png'); + expect(flags.chromeFlags).toEqual(['"--window-size 800,600"', '"--enabled-features=NetworkService,VirtualTime"']); + expect(flags.blockedUrlPatterns).toEqual(['.*x,y\\.png']); + }); }); From 3e3cc4c4e32911fea97f016908dcd2ae7b22b64e Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 13 May 2019 14:56:33 -0700 Subject: [PATCH 05/12] lint, move out of .check --- lighthouse-cli/cli-flags.js | 37 ++++++++++++----------- lighthouse-cli/test/cli/cli-flags-test.js | 5 ++- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 198dad55f398..bafb5e9e2824 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -27,7 +27,8 @@ function flatten(arr) { */ function getFlags(...manualArgv) { const y = manualArgv.length ? yargs(manualArgv) : yargs; - return y.help('help') + /** @type {LH.CliFlags} */ + const argv = y.help('help') .version(() => pkg.version) .showHelpOnFail(false, 'Specify --help for available options') @@ -161,22 +162,6 @@ function getFlags(...manualArgv) { .default('enable-error-reporting', undefined) // Undefined so prompted by default .default('channel', 'cli') .check(/** @param {LH.CliFlags} argv */ (argv) => { - // ".middleware" does not exist in this version of yargs, so do some preprocessing here. - /** @type {(keyof LH.CliFlags)[]} */ - const arrayKeys = [ - 'onlyAudits', - 'onlyCategories', - 'output', - 'plugins', - 'skipAudits', - ]; - arrayKeys.forEach(key => { - const input = /** @type {string[]} */ (argv[key]); - if (input) { - argv[key] = flatten(input.map(value => value.split(','))); - } - }); - // Lighthouse doesn't need a URL if... // - We're just listing the available options. // - We're just printing the config. @@ -196,6 +181,24 @@ function getFlags(...manualArgv) { 'For more information on Lighthouse, see https://developers.google.com/web/tools/lighthouse/.') .wrap(yargs.terminalWidth()) .argv; + + // ".middleware" does not exist in this version of yargs, so do some post-processing here. + /** @type {(keyof LH.CliFlags)[]} */ + const arrayKeysThatSupportCsv = [ + 'onlyAudits', + 'onlyCategories', + 'output', + 'plugins', + 'skipAudits', + ]; + arrayKeysThatSupportCsv.forEach(key => { + const input = /** @type {string[]} */ (argv[key]); + if (input) { + argv[key] = flatten(input.map(value => value.split(','))); + } + }); + + return argv; } module.exports = { diff --git a/lighthouse-cli/test/cli/cli-flags-test.js b/lighthouse-cli/test/cli/cli-flags-test.js index 2990f4da5b0d..b419b0ec77f3 100644 --- a/lighthouse-cli/test/cli/cli-flags-test.js +++ b/lighthouse-cli/test/cli/cli-flags-test.js @@ -45,7 +45,10 @@ describe('CLI bin', function() { '--chrome-flags="--window-size 800,600"', '--chrome-flags="--enabled-features=NetworkService,VirtualTime"', '--blockedUrlPatterns=.*x,y\\.png'); - expect(flags.chromeFlags).toEqual(['"--window-size 800,600"', '"--enabled-features=NetworkService,VirtualTime"']); + expect(flags.chromeFlags).toEqual([ + '"--window-size 800,600"', + '"--enabled-features=NetworkService,VirtualTime"', + ]); expect(flags.blockedUrlPatterns).toEqual(['.*x,y\\.png']); }); }); From ee443608b6b47895148dc664bb635997aab1d20d Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 13 May 2019 15:11:10 -0700 Subject: [PATCH 06/12] add example --- lighthouse-cli/cli-flags.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index bafb5e9e2824..8331800471d7 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -35,6 +35,9 @@ function getFlags(...manualArgv) { .usage('lighthouse ') .example( 'lighthouse --view', 'Opens the HTML report in a browser after the run completes') + .example( + 'lighthouse --only-categories=performance,pwa', + 'Only run specific categories.') .example( 'lighthouse --config-path=./myconfig.js', 'Runs Lighthouse with your own configuration: custom audits, report generation, etc.') @@ -183,7 +186,7 @@ function getFlags(...manualArgv) { .argv; // ".middleware" does not exist in this version of yargs, so do some post-processing here. - /** @type {(keyof LH.CliFlags)[]} */ + /** @type {Array} */ const arrayKeysThatSupportCsv = [ 'onlyAudits', 'onlyCategories', From 0d1a6113ef1da2091848c2c6abbac60ad2f5b198 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Thu, 16 May 2019 12:59:08 -0700 Subject: [PATCH 07/12] move example down, revise param type for flatten --- lighthouse-cli/cli-flags.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 8331800471d7..e633f62838c8 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -12,7 +12,8 @@ const pkg = require('../package.json'); const printer = require('./printer'); /** - * @param {(string | string[])[]} arr + * Remove in Node 11 - [].flatMap + * @param {Array>} arr * @return {string[]} */ function flatten(arr) { @@ -35,9 +36,6 @@ function getFlags(...manualArgv) { .usage('lighthouse ') .example( 'lighthouse --view', 'Opens the HTML report in a browser after the run completes') - .example( - 'lighthouse --only-categories=performance,pwa', - 'Only run specific categories.') .example( 'lighthouse --config-path=./myconfig.js', 'Runs Lighthouse with your own configuration: custom audits, report generation, etc.') @@ -59,6 +57,9 @@ function getFlags(...manualArgv) { .example( 'lighthouse --extra-headers=./path/to/file.json', 'Path to JSON file of HTTP Header key/value pairs to send in requests') + .example( + 'lighthouse --only-categories=performance,pwa', + 'Only run specific categories.') // List of options .group(['verbose', 'quiet'], 'Logging:') From 0067f008c6d441501f8957736e5aa36a673a6841 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Thu, 16 May 2019 13:02:13 -0700 Subject: [PATCH 08/12] add comment --- lighthouse-cli/cli-flags.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index e633f62838c8..c78e78ddae65 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -196,6 +196,8 @@ function getFlags(...manualArgv) { 'skipAudits', ]; arrayKeysThatSupportCsv.forEach(key => { + // If a key is defined as an array in yargs, the value (if provided) + // will always be a string array. const input = /** @type {string[]} */ (argv[key]); if (input) { argv[key] = flatten(input.map(value => value.split(','))); From 6f58b9007f2b9be45e7a399187b7d07b3d46554c Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Thu, 16 May 2019 13:09:08 -0700 Subject: [PATCH 09/12] fix run test --- lighthouse-cli/test/cli/run-test.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lighthouse-cli/test/cli/run-test.js b/lighthouse-cli/test/cli/run-test.js index 7c00cf961ba4..40c56889baf6 100644 --- a/lighthouse-cli/test/cli/run-test.js +++ b/lighthouse-cli/test/cli/run-test.js @@ -39,8 +39,12 @@ describe('CLI run', function() { const timeoutFlag = `--max-wait-for-load=${9000}`; const pluginsFlag = '--plugins=lighthouse-plugin-simple'; - // eslint-disable-next-line max-len - const flags = getFlags(`--output=json --output-path=${filename} ${pluginsFlag} ${timeoutFlag} ${url}`); + const flags = getFlags( + '--output=json', + `--output-path=${filename}`, + pluginsFlag, + timeoutFlag, + url); const rawResult = await run.runLighthouse(url, flags, fastConfig); @@ -90,7 +94,7 @@ describe('CLI run', function() { describe('flag coercing', () => { it('should force to array', () => { - assert.deepStrictEqual(getFlags(`--only-audits foo chrome://version`).onlyAudits, ['foo']); + assert.deepStrictEqual(getFlags('--only-audits=foo', 'chrome://version').onlyAudits, ['foo']); }); }); From e4da3a9f2ecd069281ff4415b05f179216bf345b Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Thu, 16 May 2019 19:08:07 -0700 Subject: [PATCH 10/12] revert getFlags param change --- lighthouse-cli/cli-flags.js | 17 ++++++++++------- lighthouse-cli/test/cli/cli-flags-test.js | 14 ++++++++------ lighthouse-cli/test/cli/run-test.js | 10 +++------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index c78e78ddae65..485a5817eef7 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -23,12 +23,12 @@ function flatten(arr) { } /** - * @param {string[]} manualArgv + * @param {string=} manualArgv * @return {LH.CliFlags} */ -function getFlags(...manualArgv) { - const y = manualArgv.length ? yargs(manualArgv) : yargs; - /** @type {LH.CliFlags} */ +function getFlags(manualArgv) { + // @ts-ignore yargs() is incorrectly typed as not returning itself + const y = manualArgv ? yargs(manualArgv) : yargs; const argv = y.help('help') .version(() => pkg.version) .showHelpOnFail(false, 'Specify --help for available options') @@ -197,9 +197,12 @@ function getFlags(...manualArgv) { ]; arrayKeysThatSupportCsv.forEach(key => { // If a key is defined as an array in yargs, the value (if provided) - // will always be a string array. - const input = /** @type {string[]} */ (argv[key]); - if (input) { + // will always be a string array. However, we keep argv and input as any, + // since assigning back to argv as string[] would be unsound for enums, + // for example: output is LH.OutputMode[]. + const input = argv[key]; + // Existence check + convinces TS that this is an array. + if (Array.isArray(input)) { argv[key] = flatten(input.map(value => value.split(','))); } }); diff --git a/lighthouse-cli/test/cli/cli-flags-test.js b/lighthouse-cli/test/cli/cli-flags-test.js index b419b0ec77f3..da55490120d6 100644 --- a/lighthouse-cli/test/cli/cli-flags-test.js +++ b/lighthouse-cli/test/cli/cli-flags-test.js @@ -30,24 +30,26 @@ describe('CLI bin', function() { }); it('array values support csv when appropriate', () => { - const flags = getFlags( + const flags = getFlags([ 'http://www.example.com', '--only-categories=performance,seo', '--skipAudits=unused-javascript,redirects', - '--skipAudits=bootup-time'); + '--skipAudits=bootup-time', + ].join(' ')); expect(flags.onlyCategories).toEqual(['performance', 'seo']); expect(flags.skipAudits).toEqual(['unused-javascript', 'redirects', 'bootup-time']); }); it('array values do not support csv when appropriate', () => { - const flags = getFlags( + const flags = getFlags([ 'http://www.example.com', '--chrome-flags="--window-size 800,600"', '--chrome-flags="--enabled-features=NetworkService,VirtualTime"', - '--blockedUrlPatterns=.*x,y\\.png'); + '--blockedUrlPatterns=.*x,y\\.png', + ].join(' ')); expect(flags.chromeFlags).toEqual([ - '"--window-size 800,600"', - '"--enabled-features=NetworkService,VirtualTime"', + '--window-size 800,600', + '--enabled-features=NetworkService,VirtualTime', ]); expect(flags.blockedUrlPatterns).toEqual(['.*x,y\\.png']); }); diff --git a/lighthouse-cli/test/cli/run-test.js b/lighthouse-cli/test/cli/run-test.js index 40c56889baf6..7c00cf961ba4 100644 --- a/lighthouse-cli/test/cli/run-test.js +++ b/lighthouse-cli/test/cli/run-test.js @@ -39,12 +39,8 @@ describe('CLI run', function() { const timeoutFlag = `--max-wait-for-load=${9000}`; const pluginsFlag = '--plugins=lighthouse-plugin-simple'; - const flags = getFlags( - '--output=json', - `--output-path=${filename}`, - pluginsFlag, - timeoutFlag, - url); + // eslint-disable-next-line max-len + const flags = getFlags(`--output=json --output-path=${filename} ${pluginsFlag} ${timeoutFlag} ${url}`); const rawResult = await run.runLighthouse(url, flags, fastConfig); @@ -94,7 +90,7 @@ describe('CLI run', function() { describe('flag coercing', () => { it('should force to array', () => { - assert.deepStrictEqual(getFlags('--only-audits=foo', 'chrome://version').onlyAudits, ['foo']); + assert.deepStrictEqual(getFlags(`--only-audits foo chrome://version`).onlyAudits, ['foo']); }); }); From d49db90d5a9a6321741b1b90d19333e1127c6bfd Mon Sep 17 00:00:00 2001 From: cjamcl Date: Mon, 20 May 2019 14:00:30 -0700 Subject: [PATCH 11/12] Update lighthouse-cli/cli-flags.js Co-Authored-By: Brendan Kenny --- lighthouse-cli/cli-flags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 485a5817eef7..39869de03eb6 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -186,7 +186,7 @@ function getFlags(manualArgv) { .wrap(yargs.terminalWidth()) .argv; - // ".middleware" does not exist in this version of yargs, so do some post-processing here. + // Support comma-separated values for some array flags by splitting on any ',' found. /** @type {Array} */ const arrayKeysThatSupportCsv = [ 'onlyAudits', From 2b46961b7a286206fa39cf3c2f399bc850538e7d Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 20 May 2019 14:04:17 -0700 Subject: [PATCH 12/12] edit comments --- lighthouse-cli/cli-flags.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 39869de03eb6..28f6036424e6 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -27,8 +27,9 @@ function flatten(arr) { * @return {LH.CliFlags} */ function getFlags(manualArgv) { - // @ts-ignore yargs() is incorrectly typed as not returning itself + // @ts-ignore yargs() is incorrectly typed as not accepting a single string. const y = manualArgv ? yargs(manualArgv) : yargs; + // Intentionally left as type `any` because @types/yargs doesn't chain correctly. const argv = y.help('help') .version(() => pkg.version) .showHelpOnFail(false, 'Specify --help for available options') @@ -201,7 +202,7 @@ function getFlags(manualArgv) { // since assigning back to argv as string[] would be unsound for enums, // for example: output is LH.OutputMode[]. const input = argv[key]; - // Existence check + convinces TS that this is an array. + // Truthy check is necessary. isArray convinces TS that this is an array. if (Array.isArray(input)) { argv[key] = flatten(input.map(value => value.split(','))); }