A clean, dependency-free argument parser with full static typing in TypeScript.
Define commands with options, positional arguments, extras, and subcommands—all checked at compile time.
- Fully Type-Safe: Arguments, options, extras, and subcommands are all typed at definition.
- No Dependencies: Zero runtime dependencies. Lightweight and reliable.
- Help Generation: Automatically generates descriptions and help messages.
- Controlled Execution:
No automatic
process.exit()— you control behavior explicitly. - Extensible Context: Access metadata like command name and help printer during execution.
import{command}from"args-typed";// args parser in pure tsimport{run}from"args-typed/node";// nodejs-specific utilsconstcp=command({description: "Copy single file to multiple destinations",enableHelp: true,}).positional("source","The source file").extra("destinations","The destination paths").option("f","force","Force overwrite").option("r","recursive","Copy recursively").option("S","skip","Patterns to exclude","list",parseGlob).build(([source, ...destinations],// Typed positional arguments{ force, recursive, skip },// Typed options)=>{// Main logic here});run(rm,undefined,"rm");- Full Type Safety
- Positional arguments (required and optional)
- Options (flags and values)
- Extra arguments (optional)
- Subcommands (nested)
- Zero Dependencies
- Auto-Generated Help
- Controlled Execution
- No forced
process.exit()on help or error unless explicitly coded
- No forced
- Extensible Context Handling
You can notice that your usage is incorrect before you even run the code.
For example:
constrm=command({description: "Remove files"}).extra("files","Files to remove").extra("error","extra cannot be used multiple times")// type error occurs here.option("o","option","Some option")Arbitrary positional arguments are accepted unless required is after optional.
usage: positional(name, description, parse?, required = true)
constmyRenderer=command({description: "My own renderer"}).positional("scene","The scene file to render").positional("output","The output file",/* you can pass a parse function */).positional("cwd","Change working directory",undefined,false)// optionalDuplicate names are not allowed. All options are optional.
usage: option(short?, long, description, type?, parse?)
constexec=command({description: "execute a command with env variables"}).option("h","help","Show help","boolean")/* type: boolean/scalar/list */.option("v","version","Show version")/* type="boolean" can be omitted */.option(undefined,"dry-run","Dry run")/* optional short form */.option("e","env","Environment variables","list",parseEnv)/* parse fn */Extra arguments are positional arguments after last named positional arguments.
usage: extra(name, description, parse?)
constmyCommand=command({description: "My own command"}).positional("required","required positional argument").positional("optional","optional positional argument",()=>42,false).extra("extra","extra positional argument").build(([required,optional, ...extra])=>{/* string, number, string[] */})Commands, and command groups can be nested in command groups.
constnested=command({description: "Nested command"}).build(/* ... */);constapp=commandGroup({description: "Main app"}).command("nested",nested).option("h","help","Show help")You can set a context type on building a command group/command, or creating a command group.
It's useful when you want to pass a context to a subcommand.
interfaceGlobalContext{resolve: (value: number|undefined)=>void;reject: (reason: unknown)=>void;}interfaceAppContextextendsGlobalContext{exit: number|undefined;}constapp=commandGroup<AppContext>({description: "Main app",}).option("h","help","Show help").command("subcommand",subcommand)// subcommand's context must be AppContext.build<GlobalContext>(({ help },{ context, fullName, printDescription })=>{if(help){printDescription(fullName);}return{
...context,exit: help ? 0 : undefined,};});newPromise<number|undefined>((resolve,reject)=>run(app,process.argv.slice(2),{ resolve, reject },"app",(code)=>{thrownewError(`Help with code ${code}.`);}))- Detect invalid usage at compile time
- Keep runtime simple and transparent
- Favor explicit control over hidden behavior
If you want a modern, predictable CLI parser for Node.js with zero runtime surprises, args-typed is designed for you.
MIT