Introduction
tsargp is a command-line argument parsing library that helps you write clean code.
Features
In a nutshell, here are the most interesting features offered:
Usability-wise | Functionality-wise | Presentation-wise |
---|---|---|
Zero-dependency | Word completion | Help message formatting |
Fully declarative | Option validation | Text wrapping/alignment |
Type-checked1 | Option requirements | Paragraphs and lists |
Tree-shakeable2 | Value constraints | Custom error phrases |
Browser-compatible | Name suggestions | SGR  colors and styles |
35KB minified | Subcommands | Option grouping/hiding |
ESM-native | Asynchronous callbacks | Usage statements |
Online documentation | GNU’s short-options | ANSI template literals |
From a non-functional standpoint, we are yet to perform some benchmarks, but we can attest that the codebase contains over 700 handcrafted test cases and well over 1k expect()
calls, covering virtually every line of library code.
For a list of long-desired features, see our roadmap . You may also want to check out upcoming releases  or request  a new feature — we will be happy to consider it.
Motivation
Why use this library when there are already many good argument parsers on npm ?
Among other things, it offers three distinctive features:
A declarative API
Few libraries that we know of support this3. Most others have either an imperative or a fluent interface, whereas tsargp provides a way to declare all of your command-line options in a single object
.
Data type accuracy
By relying on TypeScript , it guarantees that the values resulting from argument parsing reflect the attributes specified in the option definitions. Even JSDoc  comments are preserved in IntelliSense !
Built-in completion
The parser can emit a list of words or even fully featured suggestion objects for the purpose of tab-completion , which is then performed by the native completion engine, if one is available.
Comparison
See the differences in API style compared to other libraries.
Imperative
Usually works this way: you define an object to hold the definitive values for the options, and associate each key in this object with the respective option annotations and/or parsing function, through a series of API calls. You then make another API call to parse the command-line arguments.
Installation
Node.js
npm add tsargp
Quick Start
Define folder structure
For best modularity, you should keep command-line options separate from the main script. Below are possible folder structures for the related source code:
Flat
- cli.options.spec.ts
- cli.options.ts
- cli.ts
Define command-line options
Here we define the available options and export them by default as a single object:
import { type Options /*...*/ } from 'tsargp';
export default {
help: {
type: 'help'
names: ['-h', '--help'],
synopsis: 'Prints this help message',
}
// more definitions go here...
} as const satisfies Options;
Since we want value types to be inferred from option definitions, we do not give the object an explicit type. Instead, we declare it as const
. On the other hand, we use the satisfies
keyword to ensure that the object conforms to a valid set of option definitions.
In the documentation, you will learn about the different option types and their attributes.
Parse arguments in main script
There are multiple ways to parse the command-line arguments. Below is an example:
#!/usr/bin/env node
import { format, handleError, parse, type ParsingFlags } from 'tsargp';
import options from './cli.options.js';
try {
const flags: ParsingFlags = { format }; // inject the formatting function for help messages
const values = await parse(options, undefined, flags); // parse the command-line arguments
// do something with the option values...
} catch (err) {
// do your own handling here, if necessary, or...
// handle expected/internal error or help/version/completion message
handleError(err);
}
Notice how we include the option definitions from the sibling file. We also capture any error raised by the library and handle it based on its type. Usually, it will be either a help, version or completion message.
The documentation also shows how to return (not throw) the help and version messages, how to parse arguments into an existing object, specify parsing flags, emit warnings, and much more.
Validate options in test script
Do not forget to sanity-check the option definitions during development. Below is an example:
import { validate } from 'tsargp';
import options from './cli.options.js';
describe('cli', () => {
it('should have valid options', () => {
expect(validate(options)).resolves.toEqual({}); // no errors or warnings
// ...or you can ignore warnings that are not important to your application
});
});
The documentation also shows how to check for inconsistencies in option naming, among other things.
Enable completion (optional)
You can configure the user’s terminal to use the main script as a source of completion words or suggestions. This is handled automatically by the library. You just need to register your application with the native completion engine. Below are examples for various shells:
Bash
complete -o default -C <path_to_main_script> <cli_name>
When users install through a package manager, the latter will probably create a shim  for the script, so you should take that into account when documenting your application.
Footnotes
-
Besides TypeScript, you can leverage an external package like levn  to parse option parameters. ↩
-
The most important library components (parser, formatter, validator) can be used independently. ↩
-
At the time of writing, it’s worth mentioning meow , yargs , oclif , optionator  and command-line-usage . ↩