npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

args-typed

v0.0.5

Published

Argument parser with full static typing in TypeScript

Readme

args-typed – Type-Safe CLI Argument Parser

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.

Why Use args-typed?

  • 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.

Example Usage

import { command } from "args-typed"; // args parser in pure ts
import { run } from "args-typed/node"; // nodejs-specific utils

const cp = 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");

Key Features

  • 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
  • Extensible Context Handling

Full Type Safety

You can notice that your usage is incorrect before you even run the code.

For example:

const rm = 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")

Positional Arguments

Arbitrary positional arguments are accepted unless required is after optional.

usage: positional(name, description, parse?, required = true)

const myRenderer = 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) // optional

Options

Duplicate names are not allowed. All options are optional.

usage: option(short?, long, description, type?, parse?)

const exec = 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

Extra arguments are positional arguments after last named positional arguments.

usage: extra(name, description, parse?)

const myCommand = 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[] */ })

Subcommands

Commands, and command groups can be nested in command groups.

const nested = command({ description: "Nested command" }).build(/* ... */);
const app = commandGroup({ description: "Main app" })
  .command("nested", nested)
  .option("h", "help", "Show help")

Extensible Context Handling

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.

interface GlobalContext {
  resolve: (value: number | undefined) => void;
  reject: (reason: unknown) => void;
}

interface AppContext extends GlobalContext {
  exit: number | undefined;
}

const app = 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,
    };
  });

new Promise<number | undefined>((resolve, reject) =>
  run(app, process.argv.slice(2), { resolve, reject }, "app", (code) => {
    throw new Error(`Help with code ${code}.`);
  })
)

Design Goals Recap

  • 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.

License

MIT