Shell-ing With TypeScript

June 17, 2018  |  2 minutes to read


The shebang text for running scripts using ts-node.
Here's a TL;DR for my future self when I inevitably forget the correct shebang to use.

If you’re like me, most of your time writing bash scripts is spent Googling basic things like “how to loop in bash” while grumbling to yourself how easy this would be in a sane language like TypeScript.

As it turns out, you can write shell scripts using TypeScript, and it isn’t even that hard! Here’s how:

1. Install dependencies

If you haven’t already, download and install node.

After node is installed, install both TypeScript and ts-node globally using npm:

npm install typescript ts-node -g

2. Write your shell script in TypeScript

Create a new .ts file with a shebang as its first line that points to ts-node:

#!/usr/bin/env ts-node

Then, add some TypeScript-y stuff:

#!/usr/bin/env ts-node

console.log('Hello from TypeScript!');

3. Make your script runnable

After saving your TypeScript file, you’ll need to update its permissions to allow it to be executed:

chmod u+x your-shell-script.ts

4. Run your TypeScript file

You can now run the script as you would any other command-line utility:

> ./your-shell-script.ts

…which should result in a friendly message in your terminal:

> ./my-shell-script
Hello from TypeScript!

5. Prove to yourself that it’s working

That seemed a bit too easy - shouldn’t there be an intermediate build step in there somewhere? As a sanity check, update your .ts file with something that shouldn’t compile:

#!/usr/bin/env ts-node

console.log('Hello from TypeScript!');

// TypeScript compiler error:
// Type '4' is not assignable to type 'string'.
var myStr: string = 4;

Rerunning your script will now result in something like this:

/Users/nathanfriend/.nvm/versions/node/v7.10.0/lib/node_modules/ts-node/src/index.ts:250
    return new TSError(diagnosticText, diagnosticCodes)
           ^
TSError: ⨯ Unable to compile TypeScript:
test.ts(5,5): error TS2322: Type '4' is not assignable to type 'string'.

    at createTSError (/Users/nathanfriend/.nvm/versions/node/v7.10.0/lib/node_modules/ts-node/src/index.ts:250:12)
    at getOutput (/Users/nathanfriend/.nvm/versions/node/v7.10.0/lib/node_modules/ts-node/src/index.ts:358:40)
    at Object.compile (/Users/nathanfriend/.nvm/versions/node/v7.10.0/lib/node_modules/ts-node/src/index.ts:546:11)
    at Module.m._compile (/Users/nathanfriend/.nvm/versions/node/v7.10.0/lib/node_modules/ts-node/src/index.ts:430:43)
    at Module._extensions..js (module.js:580:10)
    at Object.require.extensions.(anonymous function) [as .ts] (/Users/nathanfriend/.nvm/versions/node/v7.10.0/lib/node_modules/ts-node/src/index.ts:433:12)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Function.Module.runMain (module.js:605:10)

This is good! You shouldn’t be able to run a TypeScript file if it contains compile-time errors.

So where is that compile step?

There isn’t one! Well, not one that you have to explicitly run, anyway. This is the magic of ts-node - it compiles and runs TypeScript files on the fly much like regular node runs JavaScript files.


Other posts you may enjoy:

I built a weird keyboard

June 26, 2023  |  14 minutes to read

Wordle Bot

January 25, 2022  |  6 minutes to read

Herding Gits

August 26, 2021  |  2 minutes to read

It's finally here! 🎉

May 7, 2021  |  1 minute to read

Capturing Alexa Errors with Sentry and GitLab

November 18, 2020  |  4 minutes to read