New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

type-flag

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

type-flag

Parse argv flags with types

  • 2.2.0
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

type-flag

Typed & tiny CLI argument parser.

Features

  • Strongly typed Parse argvs with confidence!
  • Custom types & validation Pass in any type function and the flag type will be inferred!
  • Array types Accept multiple flag values!
  • Configurable defaults Set better defaults to avoid checking whether a flag was passed in!
  • Tiny 1.3 kB minified + gzipped!

Try it out online

🔥 Looking for something more robust?

Checkout Cleye, a CLI development tool powered by type-flag. It comes with argument parsing and a beautiful --help documentation generator.

Support this project by ⭐️ starring and sharing it. Follow me to see what other cool projects I'm working on! ❤️

🚀 Install

npm i type-flag

🚦 Quick start

Here's a simple example file cli.ts:

import typeFlag from 'type-flag'

// Pass in flag schemas and automatically parse process.argv!
const parsed = typeFlag({

    // Define flags here...

    someString: String,

    someBoolean: {
        type: Boolean,
        alias: 'b'
    },

    someNumber: {
        type: Number,
        alias: 'n',
        default: 2
    },

    // Wrap type with an array to indicate accept multiple flags
    stringArray: [String],

    numberArray: {
        type: [Number]
    }
})

console.log(parsed.flags)

When you execute the file in the command-line, you can see the argvs parsed:

$ node ./cli --some-string 'hello' --some-boolean --some-number 3
> {
  someString: 'hello',
  someBoolean: true,
  someNumber: 3,
  stringArray: [],
  numberArray: []
}

parsed will have the following type:

type Parsed = {
    flags: {
        someString: string | undefined
        someBoolean: boolean
        someNumber: number
        stringArray: string[]
        numberArray: number[]
    }
    unknownFlags: {
        [flagName: string]: (string | boolean)[]
    }
    _: string[] & {
        '--': string[]
    }
}

🧑‍💻 Usage

Defining flags

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: immediately set the type
    stringFlag: String,
    numberFlag: Number,
    booleanFlag: Boolean,

    // Object syntax:
    stringFlag: {
        type: String
    }
})
Array type

To accept multiple values of a flag, use an array type by simply wrapping the type in an array:

const parsed = typeFlag({
    stringFlag: [String]
})

This allows usage like this:

$ node ./cli --string-flag A --string-flag B

# > parsed.flags.stringFlag = ['A', 'B']
Aliases

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({
    stringFlag: {
        type: String,
        alias: 's'
    }
})

This allows usage like this:

$ node ./cli -s A

# > parsed.flags.stringFlag = 'A'
Default values

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 flag type will include 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.

kebab-case flags mapped to camelCase

When passing in the flags, they can be in kebab-case and will automatically map to the camelCase equivalent.

# These two map to the same flag
$ node ./cli --someString hello --some-string world

Unknown flags

When unrecognized flags are passed in, they are either interpreted as a string or boolean depending on usage. Unknown flags are not converted to camelCase to allow for accurate error handling.

$ node ./cli --unknown-flag --unknown-flag 2

This outputs the following:

{
    unknownFlags: {
        'unknown-flag': [true, '2']
    },
    // ...
}

Arguments

All argument values are stored in the _ property.

Everything after -- (end-of-flags) is treated as arguments and will be stored in the _['--'] property.

$ node ./cli --boolean value --string "hello world" "another value" -- --string "goodbye world"

This outputs the following:

{
    _: [
        'value',
        'another value',
        '--string',
        'goodbye world',
        '--': [
            '--string',
            'goodbye world'
        ]
    ]
    // ...
}

Note: value after --boolean is parsed as an argument because the boolean flag doesn't accept a value.

Flag value delimiters

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.

👨🏻‍🏫 Examples

Using a custom type

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]

function 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
    }
    // ...
}

Optional value flag

It's common to have flags that act as a boolean when no value is passed in. This can be done by creating a custom type that returns both types.

function OptionalString(value: string) {
    if (!value) {
        return true
    }

    return value
}

const parsed = typeFlag({
    optionalString: OptionalString
})
// $ node ./cli --optional-string
parsed.flags.optionalString // => true

// $ node ./cli --optional-string hello
parsed.flags.optionalString // => 'hello'

Accepting flag values with = in it

In use-cases where flag values contain =, you can use : instead. This allows flags like --define:K=V.

$ node ./cli --define:key=value
const parsed = typeFlag({
    define: String
})

console.log(parsed.flags.define) // => 'key=value'

Dot-nested flags

$ node ./cli --env.TOKEN=123 --env.CI
type Environment = {
    TOKEN?: string
    CI?: boolean
}

function EnvironmentObject(value: string): Environment {
    const [propertyName, propertyValue] = value.split('=')
    return {
        [propertyName]: propertyValue || true
    }
}

const parsed = typeFlag({
    env: [EnvironmentObject]
})

const environment = parsed.flags.env.reduce((agg, next) => Object.assign(agg, next), {})

console.log(environment) // { TOKEN: 123, CI: true }

Inverting a boolean

To invert a boolean flag, false must be passed in with the = operator (or any other value delimiters).

$ node ./cli --boolean-flag=false

Note, without the =, the false will be parsed as a separate argument.

$ node ./cli --boolean-flag false
{
    flags: {
        booleanFlag: true
    },
    _: ['false']
}

Counting flags

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'
    }
})

console.log(parsed.flags.verbose.length)
$ node ./cli -vvv # logs 3

⚙️ API

typeFlag(flagSchema, argv, options)

Returns an object with the shape:

type Parsed = {
    flags: {
        [flagName: string]: InferredType
    }
    unknownFlags: {
        [flagName: string]: (string | boolean)[]
    }
    _: string[]
}
flagSchema

Type:

type TypeFunction = (argvValue: any) => any

type FlagSchema = {
    [flagName: string]: TypeFunction | [TypeFunction] | {
        type: TypeFunction | [TypeFunction]
        alias?: string
        default?: any
    }
}

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.

argv

Type: string[]

Default: process.argv.slice(2)

The argv array to parse.

options

Type:

type Options = {
    // Whether not to parse unknown flags (treat as arguments)
    ignoreUnknown?: boolean
}

Keywords

FAQs

Package last updated on 30 Jun 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc