-
Notifications
You must be signed in to change notification settings - Fork 991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Option's type
parameter types the option too optimistically.
#2437
Comments
The types available for client use are from DefinitelyTyped, and maintained separately. You might want to open an issue/discussion over there too. If you are not using those features, you could restrict the option processing to be less flexible (which would then match the types): |
Since // It's important that `coerce` remains typed as `undefined`,
// otherwise the options will be typed as `unknown` in the handler
function createType (type: 'string'): { type: 'string' }
function createType (type: 'number'): { type: 'number' }
function createType (type: 'boolean'): { type: 'boolean' }
function createType (type: 'string[]'): { type: 'string', array: true }
function createType (type: 'number[]'): { type: 'number', array: true }
function createType (type: 'boolean[]'): { type: 'boolean', array: true }
function createType (type: 'string' | 'number' | 'boolean' | 'string[]' | 'number[]' | 'boolean[]'): unknown {
switch (type) {
case 'string':
return {
type: 'string',
coerce: (opt: unknown): string => String(Array.isArray(opt) ? opt[opt.length - 1] : opt)
}
case 'number':
return {
type: 'number',
coerce: (opt: unknown): number => Number(Array.isArray(opt) ? opt[opt.length - 1] : opt)
}
case 'boolean':
return {
type: 'boolean',
coerce: (opt: unknown): boolean => Boolean(Array.isArray(opt) ? opt[opt.length - 1] : opt)
}
case 'string[]':
return {
type: 'string',
array: true,
coerce: (opt: unknown[]): string[] => opt.map(String)
}
case 'number[]':
return {
type: 'number',
array: true,
coerce: (opt: unknown[]): number[] => opt.map(Number)
}
case 'boolean[]':
return {
type: 'boolean',
array: true,
coerce: (opt: unknown[]): boolean[] => opt.map(Boolean)
}
}
} yargs()
.command({
command: 'cmd <pos...>',
builder: yargs => yargs
.positional('pos', { array: true })
.option('str', { ...createType('string') })
.option('str-arr', { ...createType('string[]') })
.option('num', { ...createType('number') })
.option('num-arr', { ...createType('number[]') }),
handler: args => { console.log(args) }
})
.parse(process.argv.slice(2)) This has been sufficient for me to get types to match up correctly, however there are still ways to get around it depending on your
|
When creating an option, specifying a
type
gives TypeScript some extra type info that is very useful in the command's handler:However, yargs doesn't always cast the value to what it says it will be. In the above example, it is still possible that the value of option
string
is a boolean, despite it being typed asstring | undefined
. This mismatch usually occurs when the option is specified multiple times, turning it into an array, or when the option is prefixed with--no-*
, turning it into a boolean.'string'
string | undefined
boolean
cmd --no-string
Array<string>
cmd --string="" --string=""
Array<string | boolean>
cmd --string="" --no-string
'number'
number | undefined
Array<number>
cmd --number=0 --number=0
'boolean'
boolean | undefined
'count'
number
'array'
(string | number)[] | undefined
Array<string | number | boolean>
cmd --array="" --array=0 --no-array
This has led to some unexpected runtime errors when a user unintentionally specifies an option twice.
The text was updated successfully, but these errors were encountered: