Tiny CLI flag parser whose schema returns TypeScript-inferred application values.
Use parser functions like Number, Date, enum validators, or object builders, then read clean output from parsed.flags.
No dependencies & tree-shakable (Max 1.4 kB).
Comparing parser options? See how type-flag differs from util.parseArgs(), arg, and minimist.
Tip
Looking for a full CLI framework?
Try Cleye, a CLI development tool powered by type-flag.
It adds command routing, argument parsing, and a polished --help generator on top of flag parsing.
Already a sponsor? Join the discussion in the Development repo!
npm i type-flagimport { typeFlag } from 'type-flag'
const parsed = typeFlag({
name: String,
age: Number
})
// $ my-script --name John --age 20
parsed.flags.name // string | undefined
parsed.flags.age // number | undefinedNeed a short alias?
const parsed = typeFlag({
age: {
type: Number,
alias: 'a'
}
})
// $ my-script -a 20
parsed.flags.age // number | undefinedOnly need one flag? Use getFlag() to extract a single typed value:
import { getFlag } from 'type-flag'
const age = getFlag('-a,--age', Number)
// $ my-script --age 20
age // number | undefined- Parser functions return the values your app uses: numbers, dates, enums, objects, nullable values, or validated strings.
- TypeScript infers the output from the schema.
- The schema reads like application options: add
type,alias, anddefaultnext to the flag they belong to. - Output is clean:
flags.age, notresult['--age']. - Unknown flags are separated for accurate errors or forwarding.
- Passing your own argv lets type-flag remove parsed tokens and leave the rest.
- camelCase schema keys accept kebab-case CLI input.
- It stays small and focused: a flag parser, not a CLI framework.
Pass an object where each key is the flag name and each value is a parser function.
const parsed = typeFlag({
stringFlag: String,
numberFlag: Number,
booleanFlag: Boolean,
dateFlag: value => new Date(value)
})
parsed.flags.stringFlag // string | undefined
parsed.flags.numberFlag // number | undefined
parsed.flags.booleanFlag // boolean | undefined
parsed.flags.dateFlag // Date | undefinedUse object syntax when a flag needs an alias or default value:
const parsed = typeFlag({
port: {
type: Number,
alias: 'p',
default: 3000
}
})
parsed.flags.port // numberTo get undefined in parsed flag types, enable strict or strictNullChecks.
Parser functions can validate, narrow, or transform values before your app sees them.
const possibleSizes = ['small', 'medium', 'large'] as const
type Size = typeof possibleSizes[number]
const Size = (value: string): Size => {
if (!possibleSizes.includes(value as Size)) {
throw new Error(`Invalid size: "${value}"`)
}
return value as Size
}
const parsed = typeFlag({
size: Size
})
parsed.flags.size // 'small' | 'medium' | 'large' | undefinedCustom parsers are also useful for richer values:
const EnvAssignment = (value: string) => {
const [key, rawValue = true] = value.split('=')
return { [key]: rawValue }
}
const parsed = typeFlag({
env: [EnvAssignment]
})
// $ my-script --env.TOKEN=abc --env.CI
parsed.flags.env // Array<Record<string, string | boolean>>Wrap a parser function in an array to collect repeated values.
const parsed = typeFlag({
tag: [String],
port: [Number]
})
// $ my-script --tag app --tag cli --port 3000 --port=3001
parsed.flags.tag // string[]
parsed.flags.port // number[]Flags default to undefined. Provide default to make the parsed type non-optional.
const parsed = typeFlag({
retries: {
type: Number,
default: 3
}
})
parsed.flags.retries // numberUse a factory for mutable defaults like arrays and objects:
const parsed = typeFlag({
tags: {
type: [String],
default: () => []
}
})
parsed.flags.tags // string[]Set alias to accept a single-character short flag.
const parsed = typeFlag({
verbose: {
type: [Boolean],
alias: 'v'
}
})
// $ my-script -vvv
parsed.flags.verbose.length // 3Short aliases can be grouped, so -abc is parsed as -a -b -c.
camelCase schema keys automatically accept kebab-case input.
const parsed = typeFlag({
someString: [String]
})
// $ my-script --someString hello --some-string world
parsed.flags.someString // ['hello', 'world']Unknown flags are returned separately instead of being mixed into flags.
const parsed = typeFlag({})
// $ my-script --some-flag --some-flag=1234
parsed.unknownFlags // { 'some-flag': [true, '1234'] }Wrapper CLIs often need to consume their own flags and pass everything else to another command. If you pass your own argv array, type-flag removes parsed tokens and leaves ignored tokens behind.
Ignored tokens are left in argv exactly as passed, so an ignore callback can stop parsing at a command name and preserve the full command tail for another parser.
const argv = process.argv.slice(2)
const parsed = typeFlag(
{
config: String
},
argv,
{
ignore: type => type === 'unknown-flag'
}
)
// $ wrapper --config local.json --target-flag=value
parsed.flags.config // string | undefined
argv // ['--target-flag=value']You can also stop parsing after the first positional argument:
const argv = process.argv.slice(2)
let stopParsing = false
const parsed = typeFlag(
{
verbose: [Boolean]
},
argv,
{
ignore: (type) => {
if (stopParsing) {
return true
}
if (type === 'argument') {
stopParsing = true
return true
}
}
}
)
// $ my-script --verbose ./file.js --verbose
parsed.flags.verbose // [true]
argv // ['./file.js', '--verbose']Arguments are values that are not associated with a flag. They are stored in _.
Everything after the first -- is treated as an argument and is also stored in _['--']. The -- sentinel itself is omitted; later -- tokens are ordinary arguments inside _['--'].
const parsed = typeFlag({
myFlag: [String]
})
// $ my-script --my-flag value arg1 -- --my-flag world
parsed.flags.myFlag // ['value']
parsed._ // ['arg1', '--my-flag', 'world']
parsed._['--'] // ['--my-flag', 'world']For ['one', '--', 'two'], parsed._.slice() is ['one', 'two'] and parsed._['--'] is ['two'].
Parser and framework integrations that need to rebuild this shape can use createPositionalArguments(); see the API reference below.
The characters =, :, and . delimit a value from a flag.
$ my-script --flag=value --flag:value --flag.valueThis makes define and env style flags straightforward:
const parsed = typeFlag({
define: String,
env: [String]
})
// $ my-script --define:key=value --env.TOKEN=abc
parsed.flags.define // 'key=value'
parsed.flags.env // ['TOKEN=abc']These are the supported delimiters; arbitrary delimiter characters are not treated as value separators.
Enable booleanNegation to support --no- prefixed flags for booleans.
const parsed = typeFlag({
verbose: Boolean
}, process.argv.slice(2), { booleanNegation: true })
// $ my-script --no-verbose
parsed.flags.verbose // falseLast value wins:
// $ my-script --verbose --no-verbose
parsed.flags.verbose // false
// $ my-script --no-verbose --verbose
parsed.flags.verbose // trueThe --no- prefix only applies to flags defined as Boolean. For non-boolean or unregistered flags, --no-<name> is treated as an unknown flag.
You can also pass false explicitly with a value delimiter:
// $ my-script --verbose=false
parsed.flags.verbose // falseWithout a value delimiter, false is a separate argument:
// $ my-script --verbose false
parsed.flags.verbose // true
parsed._ // ['false']A parser can return different types depending on whether a value was provided.
const OptionalString = (value: string) => {
if (!value) {
return true
}
return value
}
const parsed = typeFlag({
string: OptionalString
})
// $ my-script --string
parsed.flags.string // true
// $ my-script --string hello
parsed.flags.string // 'hello'A flag name can itself be a single character. It matches -x but not --x.
const parsed = typeFlag({
x: Number,
y: Number
})
// $ my-script -x 10 -y 20
parsed.flags.x // 10
parsed.flags.y // 20Because --x is reserved for long flags, you can declare both a single-character flag and a long-form flag as independent entries:
const parsed = typeFlag({
h: Boolean,
help: Boolean
})
// $ my-script -h --help --h
parsed.flags.h // true
parsed.flags.help // true
parsed.unknownFlags.h // [true]Use a single-character name when the short form is the flag (-x/-y coordinates, -h vs --help). Use an alias when --verbose and -v should set the same value.
A single-character flag cannot have an alias.
Use getFlag() when you only need one typed flag and want to leave the rest of argv available for another parser or command.
const argv = process.argv.slice(2)
const port = getFlag('-p,--port', Number, argv)
// $ my-script --port 3000 -- --forwarded
port // number | undefined
argv // ['--', '--forwarded']Wrap the parser in an array to retrieve all matching values.
const tag = getFlag('--tag', [String])
// $ my-script --tag app --tag cli
tag // string[]Returns an object with the shape:
type Parsed = {
flags: {
[flagName: string]: InferredType
}
unknownFlags: {
[flagName: string]: (string | boolean)[]
}
_: string[] & {
'--': string[]
}
}Type:
type TypeFunction = (...args: any[]) => unknown
type FlagSchema = {
[flagName: string]: TypeFunction | [TypeFunction] | {
type: TypeFunction | [TypeFunction]
alias?: string
default?: unknown | (() => unknown)
}
}An object containing flag schema definitions. The key is the flag name, and the value is either a parser function, an array parser, or an object containing the parser plus options.
Type: string[]
Default: process.argv.slice(2)
The argv array to parse. If you pass your own array, it is mutated to remove parsed flags and arguments.
Type:
type Options = {
ignore?: (
type: 'known-flag' | 'unknown-flag' | 'argument',
flagOrArgv: string,
value: string | undefined
) => boolean | void
booleanNegation?: boolean
}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. If you pass your own array, it is mutated to remove the parsed flag and its value.
Builds the same positional argument shape returned as parsed._. This is mainly useful for parser or framework integrations that already preserved an argv tail and need to expose type-flag-compatible positionals.
Type:
const createPositionalArguments: (argv: readonly string[]) => PositionalArguments
type PositionalArguments = string[] & {
'--': string[]
}The first -- token is the delimiter. It is omitted from the returned array, and everything after it is also exposed on positionals['--'].
createPositionalArguments() does not mutate the input array.
Choose type-flag when you want a tiny parser whose schema returns the values your app actually uses.
const parsed = typeFlag({
age: {
type: Number,
alias: 'a',
default: 18
}
})
parsed.flags.age // numberThat one schema owns the app key, parser, alias, default, and TypeScript output. This is the main reason to choose type-flag: less post-processing, fewer raw option strings in app code, and a parsed result shaped like your application.
Where type-flag shines:
- App-ready values: parser functions return numbers, dates, enums, objects, nullable values, or validated strings.
- Readable schemas: keep
type,alias, anddefaultnext to the app key they configure. - TypeScript confidence: bundled types infer
flagsfrom parser return types and defaults. - Forwarding wrappers: known flags, unknown flags, positionals, and leftover argv stay easy to separate.
- CLI-friendly naming: camelCase schema keys accept kebab-case input like
--some-flag.
How that compares:
| Alternative | Best fit | What type-flag optimizes instead |
|---|---|---|
util.parseArgs() |
Built-in strict parsing when string and boolean values are enough. |
Parser functions for app-native values like numbers, dates, enums, and validators. |
arg |
Strict parser functions and bundled types with raw keys like result['--port']. |
App-shaped schemas and output: type, alias, default, and flags.port stay together. |
minimist |
Quick permissive parsing for scripts where heuristic coercion is acceptable; TypeScript users rely on separate broad @types/minimist types. |
Explicit schemas with bundled, schema-inferred TypeScript output. |
Cleye, commander, yargs, cac, meow |
Full CLI apps that need commands, help text, version flags, validation UX, or app structure. | Focused flag parsing that stays small and easy to embed. |
A few trade-offs are intentional:
- It does not parse POSIX-style short string values like
-ovalue. - It treats missing string values as
""; strict parsers likeparseArgs()andargthrow. - For negative numeric values, use inline values like
--count=-1; space-separated--count -1is parsed as a flag-like token today.
This package ships with a built-in agent skill for AI coding assistants. Set up skills-npm in your project to use it.


