Strongly typed command-line arguments parser.
No dependencies & tree-shakable (Max 1.4 kB).
Tip
Looking for something more robust? 👀
Try Cleye—a CLI development tool powered by type-flag.
In addition to flag parsing, it supports argument parsing and has a beautiful --help
documentation generator.
Already a sponsor? Join the discussion in the Development repo!
npm i type-flag
Let's say you want to create a script with the following usage:
$ my-script --name John --age 20
Here's how easy it is with type-flag:
import { typeFlag } from 'type-flag'
const parsed = typeFlag({
name: String,
age: {
type: Number,
alias: 'a'
}
})
console.log(parsed.flags.name) // 'John'
console.log(parsed.flags.age) // 20
You can also get unknown flags and arguments from the parsed
object:
// object of unknown flags passed in
console.log(parsed.unknownFlags)
// arguments
console.log(parsed._)
Want something even simpler?
type-flag also exports a getFlag
function that returns a single flag value.
import { getFlag } from 'type-flag'
const name = getFlag('--name', String)
const age = getFlag('-a,--age', Number)
console.log(name) // 'John'
console.log(age) // 20
These are quick demos but type-flag can do so much more:
- Accept multiple flag values
- Flag operators (e.g.
=
) for explicitly passing in a value - Parse unknown flags
- Parse alias groups (e.g.
-abc
)
Keep reading to learn more!
Pass in an object where the key is the flag name and the value is the flag type—a parser function that takes in a string and parses it to that type. Default JavaScript constructors should be able to cover most use-cases: String, Number, Boolean, etc.
The value can also be an object with the type
property as the flag type.
typeFlag({
// Short-hand
stringFlag: String,
numberFlag: Number,
booleanFlag: Boolean,
// Object syntax:
stringFlag: {
type: String
}
})
To accept multiple values of a flag, wrap the type with an array:
const parsed = typeFlag({
myFlag: [String]
})
// $ node ./cli --my-flag A --my-flag B
parsed.flags.myFlag // => ['A', 'B']
Flags are often given single-character aliases for shorthand usage (eg. --help
to -h
). To give a flag an alias, use the object syntax and set the alias
property to a single-character name.
typeFlag({
myFlag: {
type: String,
alias: 'm'
}
})
// $ node ./cli -m hello
parsed.flags.myFlag // => 'hello'
Flags that are not passed in will default to being undefined
. To set a different default value, use the object syntax and pass in a value as the default
property. When a default is provided, the return type will reflect that instead of undefined
.
When using mutable values (eg. objects/arrays) as a default, pass in a function that creates it to avoid mutation-related bugs.
const parsed = typeFlag({
someNumber: {
type: Number,
default: 1
},
manyNumbers: {
type: [Number],
// Use a function to return an object or array
default: () => [1, 2, 3]
}
})
To get undefined
in the parsed flag type, make sure strict
or strictNullChecks
is enabled.
When passing in the flags, they can be in kebab-case and will automatically map to the camelCase equivalent.
const parsed = typeFlag({
someString: [String]
})
// $ node ./cli --someString hello --some-string world
parsed.flags.someString // => ['hello', 'world']
When unrecognized flags are passed in, they are interpreted as a boolean, or a string if explicitly passed in. Unknown flags are not converted to camelCase to allow for accurate error handling.
const parsed = typeFlag({})
// $ node ./cli --some-flag --some-flag=1234
parsed.unknownFlags // => { 'some-flag': [true, '1234'] }
Arguments are values passed in that are not associated with any flags. All arguments are stored in the _
property.
Everything after --
(end-of-flags) is treated as an argument (including flags) and will be stored in the _['--']
property.
const parsed = typeFlag({
myFlag: [String]
})
// $ node ./cli --my-flag value arg1 -- --my-flag world
parsed.flags.myFlag // => ['value']
parsed._ // => ['arg1', '--my-flag', 'world']
parsed._['--'] // => ['--my-flag', 'world']
The characters =
, :
and .
are reserved for delimiting the value from the flag.
$ node ./cli --flag=value --flag:value --flag.value
This allows for usage like --flag:key=value
or --flag.property=value
to be possible.
When type-flag
iterates over the argv array, it removes the tokens it parses out via mutation.
By default, type-flag
works on a new copy of process.argv.slice(2)
so this doesn't have any side-effects. But if you want to leverage this behavior to extract certain flags and arguments, you can pass in your own copy of process.argv.slice(2)
.
This may be useful for filtering out certain flags before passing down the argv
to a child process.
Sometimes it may be undesirable to parse unknown flags. In these cases, you can ignore them so they're left unparsed in the argv
array.
const argv = process.argv.slice(2)
const parsed = typeFlag(
{},
argv,
{
ignore: type => type === 'unknown-flag'
}
)
// $ node ./cli --unknown=hello
parsed._ // => []
argv // => ['--unknown=hello']
Similarly to how Node.js only reads flags passed in before the first argument, type-flag can be configured to ignore everything after the first argument.
const argv = process.argv.slice(2)
let stopParsing = false
const parsed = typeFlag(
{
myFlag: [Boolean]
},
argv,
{
ignore: (type) => {
if (stopParsing) {
return true
}
const isArgument = type === 'argument'
if (isArgument) {
stopParsing = isArgument
return stopParsing
}
}
}
)
// $ node ./cli --my-flag ./file.js --my-flag
parsed.flags.myFlag // => [true]
argv // => ['./file.js', '--my-flag']
Basic types can be set using built-in functions in JavaScript, but sometimes you want to a new type, narrow the type, or add validation.
To create a new type, simply declare a function that accepts a string argument and returns the parsed value with the expected type.
In this example, the size
flag is enforced to be either small
, medium
or large
.
const possibleSizes = ['small', 'medium', 'large'] as const
type Sizes = typeof possibleSizes[number]
const Size = (size: Sizes) => {
if (!possibleSizes.includes(size)) {
throw new Error(`Invalid size: "${size}"`)
}
return size
}
const parsed = typeFlag({
size: Size
})
parsed
resolves to the following type:
type Parsed = {
flags: {
size: 'small' | 'medium' | 'large' | undefined
}
// ...
}
To create a string flag that acts as a boolean when nothing is passed in, create a custom type that returns both types.
const OptionalString = (value: string) => {
if (!value) {
return true
}
return value
}
const parsed = typeFlag({
string: OptionalString
})
// $ node ./cli --string
parsed.flags.string // => true
// $ node ./cli --string hello
parsed.flags.string // => 'hello'
In use-cases where flag values contain =
, you can use :
instead. This allows flags like --define:K=V
.
const parsed = typeFlag({
define: String
})
// $ node ./cli --define:key=value
parsed.flags.define // => 'key=value'
type Environment = {
TOKEN?: string
CI?: boolean
}
const EnvironmentObject = (value: string): Environment => {
const [propertyName, propertyValue] = value.split('=')
return {
[propertyName]: propertyValue || true
}
}
const parsed = typeFlag({
env: [EnvironmentObject]
})
const env = parsed.flags.env.reduce(
(agg, next) => Object.assign(agg, next),
{}
)
// $ node ./cli --env.TOKEN=123 --env.CI
env // => { TOKEN: 123, CI: true }
To invert a boolean flag, false
must be passed in with the =
operator (or any other value delimiters).
const parsed = typeFlag({
booleanFlag: Boolean
})
// $ node ./cli --boolean-flag=false
parsed.flags.booleanFlag // => false
Without explicitly specfying the flag value via =
, the false
will be parsed as a separate argument.
// $ node ./cli --boolean-flag false
parsed.flags.booleanFlag // => true
parsed._ // => ['false']
To create an API where passing in a flag multiple times increases a count (a pretty common one is -vvv
), you can use an array-boolean type and count the size of the array:
const parsed = typeFlag({
verbose: {
type: [Boolean],
alias: 'v'
}
})
// $ node ./cli -vvv
parsed.flags.verbose.length // => 3
Returns an object with the shape:
type Parsed = {
flags: {
[flagName: string]: InferredType
}
unknownFlags: {
[flagName: string]: (string | boolean)[]
}
_: string[]
}
Type:
type TypeFunction = (...args: any[]) => unknown
type FlagSchema = {
[flagName: string]: TypeFunction | [TypeFunction] | {
type: TypeFunction | [TypeFunction]
alias?: string
default?: unknown
}
}
An object containing flag schema definitions. Where the key is the flag name, and the value is either the type function or an object containing the type function and/or alias.
Type: string[]
Default: process.argv.slice(2)
The argv array to parse. The array is mutated to remove the parsed flags.
Type:
type Options = {
// Callback to skip parsing on certain argv tokens
ignore?: (
type: 'known-flag' | 'unknown-flag' | 'argument',
flagOrArgv: string,
value: string | undefined,
) => boolean | void
}
Type: string
A comma-separated list of flag names to parse.
Type:
type TypeFunction = (...args: any[]) => unknown
type FlagType = TypeFunction | [TypeFunction]
A function to parse the flag value. Wrap the function in an array to retrieve all values.
Type: string[]
Default: process.argv.slice(2)
The argv array to parse. The array is mutated to remove the parsed flags.