Using tsm as a shebang interpreter in TypeScript

Featured on Hashnode
Using tsm as a shebang interpreter in TypeScript

Quick Summary

In this tutorial, we will learn how to use the tsm (TypeScript Module Loader) npm package as a shebang interpreter in TypeScript. This npm package enables you to run your typescript code in your terminal. And to understand this better, we'd be creating a simple shopping cart with TypeScript.

Goal

At the end of this tutorial, users should better understand the TypeScript programming language and how to use the tsm npm package as a shebang interpreter to run TypeScript code in the terminal.

Prerequisite

  • Node.js version >= 12.x.x installed on your local machine.

  • Good knowledge of JavaScript language.

  • Basic knowledge of TypeScript Language.

  • Ensure that npm or yarn is also installed as the package manager.

TypeScript

TypeScript is a programming language that can be used as an alternative to JavaScript. It is also known as a superset of JavaScript, which means that it extends the JavaScript language with new features and syntax, so it can do all of the things JavaScript can do.

It is important to note that browsers by default can not understand TypeScript, so when TypeScript code is written, it has to be compiled into JavaScript, which is understandable by the browser.

Why use TypeScript?

TypeScript language is less prone to errors due to the extra features it comes with; some of these features are:

  • Type System: The type system of TypeScript is incredibly rich and includes: interfaces, enums, hybrid types, generics, union/intersection types, access modifiers and much more. TypeScript also enforces strict types; for example, if you declare a variable in TypeScript to be a certain type like a string, you can not change the type of that variable later on to a number or a boolean.
  • Support for modern JavaScript features: Modern features in later versions of JavaScript that might not yet be fully supported in browsers. TypeScript allows for the use of many of the latest ECMAScript features and translates them to older ECMAScript targets of your choosing. This means you can safely use the latest features like arrow functions, let, const and modules.

In our project, we will be using the tsm package as an interpreter to run our TypeScript project in the terminal without compiling our TypeScript code into JavaScript.

TSM

Tsm is a package that can be used in your project in a couple of ways:

  1. As a node CLI replacement
  2. As a CommonJS --require hook
  3. As an ESM --loader
  4. As a shell shebang interpreter

Tsm can be downloaded as a project dependency.

$ npm install --save-dev tsm

or globally

$ npm install --global tsm

Our focus is to write shell scripts with tsm as the interpreter, so we'd be downloading tsm globally on our computer.

Shebang

If this is your first encounter with the term 'shebang', I'm assuming you think it's a word to describe a complicated programming procedure; fortunately, it isn't.

Simply put, Shebang is a special kind of comment that tells your computer's operating system what command line interpreter (shell) to use to execute a file. The shebang must be the first line of the file and start with #!.

Examples of command line interpreters are bash, sh, C shell, e.t.c. To find out the name of your terminal default interpreter/shell, run echo $0 for Unix systems.

echo $0
-bash

How to write a shebang comment

Imagine a situation where you have both bash and sh on your computer and your terminal default is bash and you intend to use sh to execute a script. To ensure that your script will be interpreted with sh you’ll need to specify the executable path using a shebang comment.

You can either write the shebang comment by writing the absolute path to the sh binary, like this #!/bin/sh or by using the env utility like #!/usr/bin/env sh. The latter method uses the env command to find the path to the sh executable.

Either way, you have successfully directed your computer OS to utilize the sh command line interpreter to execute the script.

Building a simple shopping cart

Now we understand how to write a shebang comment to direct our computer OS to use the tsm binary to execute our TypeScript file. Let's get started on our mini-project.

Ordinarily, after writing TypeScript code, it has to be compiled to JavaScript before the browser can understand it. To demonstrate this, let's create the shopping cart without executing the TypeScript code directly with the tsm binary.

Let's start by creating a folder with TypeScript and HTML files. Screen Shot 2021-11-05 at 11.21.14 AM.png

Next we import the TypeScript file into the index.html file.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Shopping Cart</title>
</head>
<body>
    <script src="script.ts"></script>
</body>
</html>

Next we create the shopping cart with a function to add items to cart in the script.ts file.

function Cart() {
    const basket : Array<object> = [];

    // prototype that adds item to the cart
    const addItem = (itemName:string, quantity:number, price:number) => {

        interface Product {
            name: string;
            quantity: number
            price: number;
          }
      let exist : boolean = false;
      // looping through to check if item already exist using item name
      basket.forEach((element: Product) => {
        if (element.name.toLowerCase() === itemName.toLowerCase()) {
          exist = true;
        }
      });
      if (exist) {
        console.log("Item already exists");
      } else {
        //pushing into basket if item does not already exist
        basket.push({ name: itemName, quantity: quantity, price: price });
      }
      return basket;
    };

    addItem('bread',3,89)
    console.log(basket)
}
Cart()

Now the function has been created, let's take a peek in our browser console for inspection.

An error message is displayed in the console.

Refused to execute script from '127.0.0.1:5500/script.ts' because its MIME type ('video/mp2t') is not executable.

This is because the browser can not understand TypeScript, it has to be compiled to JavaScript before our code can be executed. To compile our TypeScript code, we need to install the TypeScript compiler into our computer by running the code block below.

npm install -g typescript

Now we've installed the TypeScript compiler globally, we can head on to our terminal in the exact directory as our TypeScript file and run tsc script.ts to compile the TypeScript code to JavaScript.

tsc script.ts

Once this is done, the code is compiled into a script.js file.

Screen Shot 2021-11-07 at 1.08.30 PM.png

Import the script.js file into index.html instead of script.ts.

<script src="script.js"></script>

Immediately, the browser understands javascript and runs the code.

Screen Shot 2021-11-07 at 1.23.00 PM.png

Using tsm to execute TypeScript in the terminal directly

Using the tsm binary, we can execute our script.ts file directly in our terminal without compiling it into javascript; we tell the shell to use tsm binary to execute the script.ts file.

A shebang directive is written at the first line of the script.ts file.

#!/usr/bin/env tsm

function Cart() {
    const basket : Array<object> = [];

    // prototype that adds item to the cart
    const addItem = (itemName:string, quantity:number, price:number) => {

        interface Product {
            name: string;
            quantity: number
            price: number;
          }
      let exist : boolean = false;
      // looping through to check if item already exist using item name
      basket.forEach((element: Product) => {
        if (element.name.toLowerCase() === itemName.toLowerCase()) {
          exist = true;
        }
      });
      if (exist) {
        console.log("Item already exists");
      } else {
        //pushing into basket if item does not already exist
        basket.push({ name: itemName, quantity: quantity, price: price });
      }
      return basket;
    };

    addItem('bread',3,89)
    console.log(basket)
}
Cart()

We successfully added instructions on how the TypeScript file should be executed by including the shebang comment at the beginning of the code.

To get to the big part and execute our code, we have to modify the permissions of our file and mark it as executable. We do this by running the block of code below.

chmod +x script.ts

Finally, we can run our script.ts file by simply running ./script.ts in our terminal.

./script.ts

Our code is run, but there's an error before it.

ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time

(Use node --trace-warnings ... to show where the warning was created)

[ { name: 'bread', quantity: 3, price: 89 } ]

When writing the shebang, you can also include CLI flags; in our case, we can forward the --trace-warnings to node by replacing the initial shebang #!/usr/bin/env tsm with #!/usr/bin/env tsm --trace-warnings

After doing this and script.ts executes, a block of ExperimentalWarning text is from the --trace-warnings argument. This flag is forwarded to node, which prints this output natively.

Conclusion

In this article, TypeScript was introduced as a superset of JavaScript and its benefits over JavaScript were listed. Furthermore, it was explained that the browser is unable to understand typescript and it needs to be compiled into JavaScript before it can be understood by the browser.

With the tsm package installed, a shebang can be written on the first line of your TypeScript file and it will run typescript files in a single command without compiling it to a separate JavaScript file.

Resources