Matthew Swensen Principal software engineer and open source enthusiast.

Using tsm as a shebang interpreter in TypeScript

3 min read 1036

using tsm shebang interpreter feature image

TypeScript has taken the web development world by storm, becoming increasingly popular for enterprise projects and solo developers alike.

Executing TypeScript code typically involves transpiling it down to vanilla JavaScript for use in common environments like Node.js or the browser. This approach still represents the vast majority of how TypeScript programs are run, but a few projects have emerged that allow developers to run their TypeScript code directly.

tsm is a brand-new player in this category and represents a step forward in the broader TypeScript/JavaScript ecosystem.

What is tsm?

tsm is a TypeScript module loader for Node.js, created by Luke Edwards. It expands Node.js to provide native TypeScript support without the need to transpile it to JavaScript first.

Rather, tsm uses esbuild under the hood to quickly transpile TypeScript sources into JavaScript on the fly before passing it to the Node.js runtime. This approach essentially abstracts away the typical transpilation step, making it transparent to the user.

Should you use tsm?

The primary and most obvious advantage to using tsm is that developers don’t need to worry about TypeScript transpilation. This means that development feedback loops are shorter, build tooling is simpler, and deployment is more straightforward. This is particularly convenient for small projects like one-off scripts.

Because tsm runs on top of Node.js, developers familiar with Node will find using tsm natural and easy to incorporate TypeScript into their projects with a simple step.

For example, esbuild has built-in source map support, which means that TypeScript programs that run under tsm can be debugged with the standard V8 debugging protocol (included with Node.js) and popular debugging tools like VS Code.

Additionally, the vast ecosystem of npm packages and Node built-in APIs are available to TypeScript running with tsm.

The disadvantages of tsm

One relatively small trade-off of using tsm is that it must be installed, in addition to Node.js itself, on the system that needs to run the TypeScript code.

We made a custom demo for .
No really. Click here to check it out.

This can be an inconvenience if distributing scripts meant to be run directly from the shell (like when using tsm as the interpreter for a script via shebang interpreter directive), but not a problem for fully packaged Node.js modules.

Other than that, there aren’t too many downsides to giving tsm a try, assuming there’s an appetite for new and emerging (and therefore, potentially changing) tech.

Unlike other elements of a project’s technology stack (such as databases, programming languages, frameworks, etc.), the nice thing about JavaScript build tools is that they can be swapped out later with minimal impact on the application’s source code itself.

How to use tsm in a Node.js project

There are a few different ways to hook tsm into a Node.js project.

As a replacement for the node executable

Assuming you have tsm installed globally (via npm -- global install tsm or yarn global add tsm), you can run TypeScript files using the tsm executable, like so:

tsm index.ts

For local installations, the tsm executable can be found at ./node_modules/.bin/tsm.

Note that any command-line flags will be passed through to node, which is convenient for experimental features, debugging configuration, and the like.

With a shebang interpreter directive

Because tsm includes an executable, it can be used in a shebang directive like any other script interpreter. Shebang directives are formatted as such: !#<path/to/interpreter> [arguments].

They appear at the first line of the file and tell the operating system which program to use to parse and execute the rest of the file.

For example:

#!/usr/local/bin/tsm

function print(message: string): void {
  console.log(message);
}

print('hello from TypeScript!');

Using the --require or --loader hooks

If you still need to use node directly rather than the tsm executable, you can use the --require or --loader hooks to instruct node to use tsm to load TypeScript files. tsm needs to be installed as a local dependency (rather than globally) for this to work.

node --require tsm index.ts
node --loader tsm index.ts
Demo: using tsm with require hook
Demo: using tsm with require hook

Deno: An alternative to tsm

tsm isn’t the only way to achieve transpilation-less executable TypeScript. Ryan Dahl, the creator of Node.js, has released a project called Deno that ships with TypeScript support out of the box.

Like Node.js, Deno is built on top of the V8 JavaScript engine and seeks to overcome some of Node.js’s design flaws.

Like Node.js and tsm, Deno can be used directly as an executable:

deno index.ts

It can also be used in a shebang directive:

!#/usr/local/bin/deno

...

However, code written for Node.js in TypeScript may not be portable to Deno, as Deno provides its own standard library as a replacement for the Node.js APIs. It also has its own paradigm for distributing modules rather than utilizing npm. Check out Deno’s standard library and API docs for more details.

Conclusion

tsm is a great way to integrate TypeScript into a Node.js project quickly and seamlessly. Because it’s built upon Node.js, tsm users can leverage the vast ecosystem of packages in npm and the powerful Node.js APIs, all from within TypeScript without needing to set up a complicated build process.

Check out tsm’s GitHub repo and its dependents list to see what people are using it for. Some notable examples include:

  • create-figma-plugin: a comprehensive toolkit for developing plugins and widgets for Figma and FigJam. It is written in TypeScript and uses tsm for its development process
  • tinyhttp: a modern web framework written in TypeScript. It uses tsm in its documentation examples as a simple way to get users up and running quickly
  • nanostores: a tiny state manager for JavaScript projects. tsm is used in the development cycle for this module

In short, use tsm when you want to leverage Node’s vast ecosystem from within TypeScript or integrate TypeScript into an existing Node codebase. If you don’t have many npm-only dependencies and want to enjoy the benefits of Deno’s security model, consider using Deno for your next server-side TypeScript project.

Happy coding!

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

Writing a lot of TypeScript? Watch the recording of our recent TypeScript meetup to learn about writing more readable code.

TypeScript brings type safety to JavaScript. There can be a tension between type safety and readable code. Watch the recording for a deep dive on some new features of TypeScript 4.4.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Matthew Swensen Principal software engineer and open source enthusiast.

Leave a Reply