As a developer, creating command-line interface (CLI) tools can greatly enhance productivity and automate repetitive tasks. With TypeScript’s robust type system and modern JavaScript features, we can build reliable and maintainable CLI tools. In this guide, I’ll walk you through the process of building a cross-platform CLI tool in TypeScript and compiling it into binaries for macOS, Windows, and Linux. This approach ensures that your tool can be easily distributed and used across different environments.
Thank me by sharing on Twitter 🙏
Setting Up Your CLI Project
Before we dive into the code, ensure you have a basic TypeScript project set up with the necessary dependencies. You should have typescript, ts-node, and @types/node installed, along with a tsconfig.json file configured for TypeScript compilation. Additionally, you’ll need commander for handling command-line arguments and pkg for compiling your TypeScript code into binaries.
Writing the CLI Script
The heart of our CLI tool lies in the script that handles user input and executes the desired functionality. We’ll use the commander library to manage command-line arguments and options.
First, create a src/index.ts file. This file will serve as the entry point for our CLI tool. Here’s a basic example of a CLI tool that greets a user:
import { Command } from 'commander';
const program = new Command();
program
.name('my-cli-tool')
.description('An example CLI tool')
.version('1.0.0');
program
.command('greet <name>')
.description('Greet someone')
.action((name) => {
console.log(`Hello, ${name}!`);
});
program.parse(process.argv);In this script, we define a CLI tool named my-cli-tool with a single command greet that takes a name parameter and prints a greeting message.
Apple in China: The Capture of the World's Greatest Company
$19.68 (as of October 23, 2025 18:10 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)If Anyone Builds It, Everyone Dies: Why Superhuman AI Would Kill Us All
$19.68 (as of October 23, 2025 18:10 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Start with Why: How Great Leaders Inspire Everyone to Take Action
$13.38 (as of October 23, 2025 18:10 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Configuring TypeScript
To ensure our TypeScript code compiles correctly, we need to configure our tsconfig.json file. Here’s the configuration I use:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}This configuration sets the target JavaScript version to ES6 and specifies the output directory for compiled files as dist. It also ensures strict type-checking and compatibility with CommonJS modules.
Next, add a build script in your package.json to compile the TypeScript code:
"scripts": {
"build": "tsc"
}Run the build script to compile your TypeScript code:
npm run buildThis command will compile your TypeScript files and place the resulting JavaScript files in the dist directory.
Preparing for Packaging
Before we package our CLI tool, we need to ensure the bin property in our package.json points to the correct entry point. Here’s how to update your package.json:
{
"name": "my-cli-tool",
"version": "1.0.0",
"description": "An example CLI tool",
"main": "dist/index.js",
"bin": {
"my-cli-tool": "./dist/index.js"
},
"scripts": {
"build": "tsc"
},
"dependencies": {
"commander": "^9.0.0"
},
"devDependencies": {
"pkg": "^5.5.1",
"typescript": "^4.0.0",
"ts-node": "^10.0.0",
"@types/node": "^16.0.0"
}
}The bin property points to the compiled index.js file in the dist directory, which serves as the entry point for the CLI tool.
Compiling to Binaries
With our project set up and configured, we can now compile our CLI tool into binaries for different platforms using pkg. Run the following command to generate binaries for macOS, Linux, and Windows:
npx pkg . --out-path dist --targets node18-macos-x64,node18-linux-x64,node18-win-x64This command will create three binaries in the dist directory, one for each target platform.
Testing Your CLI Tool
After compiling the binaries, it’s crucial to test them to ensure they work as expected on each platform. Here’s how you can test the binaries:
- On macOS:
./dist/my-cli-tool-macos greet Alice- On Linux:
./dist/my-cli-tool-linux greet Bob- On Windows:
.\dist\my-cli-tool-win.exe greet CarolThese commands should print a greeting message for each name provided.
Conclusion
In this guide, we walked through the process of creating a cross-platform CLI tool in TypeScript and compiling it into binaries for macOS, Windows, and Linux. By leveraging the power of TypeScript and the commander library, we built a simple yet effective CLI tool. Using pkg, we packaged our tool into standalone binaries that can be easily distributed and used across different environments.
Creating CLI tools in TypeScript not only improves productivity but also ensures that our tools are type-safe and maintainable. With this foundation, you can expand your CLI tool with additional commands and features to suit your needs.


