← Back to TypeScript Course | Chapter 28: Real World Projects | Lesson 4 of 14

CLI Tool with TypeScript

TypeScript can be used to build command-line tools running on Node.js. Typed argument parsing and service functions make CLI code easier to maintain.

Core Concept

A typed CLI tool benefits from TypeScript by making its argument parsing and command structure type-safe, so a typo in an option name or a wrong flag type is caught before the tool ships, not when a user hits it.

Example: Core Concept

typescript
interface Options {
  port: number;
  verbose: boolean;
}
function parseArgs(args: string[]): Options {
  return { port: 3000, verbose: args.includes("--verbose") };
}
console.log(parseArgs(["--verbose"]));

Basic Setup

A basic setup uses a library like commander or yargs (both ship TypeScript types) to define commands and options, compiling the .ts source to a runnable Node script via tsc.

Example: Basic Setup

typescript
// npm install commander
// import { program } from "commander";
console.log("commander/yargs ship TypeScript types for CLI parsing");

Typed Example

A typed example: program.option('-p, --port <number>', 'port number', Number) combined with yargs's inferred Arguments type means argv.port is known to be a number everywhere it's used, not a raw string that needs manual parsing.

Example: Typed Example

typescript
// program.option('-p, --port <number>', 'port number', Number);
interface Argv {
  port: number;
}
const argv: Argv = { port: 8080 };
console.log(argv.port); // known to be a number, not a raw string

Project Usage

In a real project, a CLI's typed command handlers stay maintainable as more commands and flags get added over time, since the compiler catches a handler that forgot to account for a newly-added required option.

Example: Project Usage

typescript
interface Command {
  name: string;
  run: (args: string[]) => void;
}
const commands: Command[] = [
  { name: "build", run: () => console.log("Building...") },
];
commands[0].run([]);

Best Practices

Add a bin field to package.json pointing at your compiled entry file (with a #!/usr/bin/env node shebang) so the tool can be installed globally and run as a real command once built.

Example: Best Practices

typescript
// package.json: { "bin": { "mytool": "./dist/cli.js" } }
// dist/cli.js starts with: #!/usr/bin/env node
console.log("bin field + shebang lets the tool run as a global command");

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.