Using TypeScript Decorators with esbuild

Written by theBenForce | Published 2023/05/13
Tech Story Tags: typescript | esbuild | devops | devops-tools | typescript-tutorial | software-development | decorators | optimization | web-monetization

TLDRTo get decorators working in esbuild, use the `esbuild-plugin-tsc` plugin and set the `format` property to `cjs`.via the TL;DR App

Just about every developer that I meet has one, or more, side-projects that they work on. My primary side project (an Alexa skill called Movie Quiz) has helped me learn several new technologies over the years. Working on this skill has taught me everything from DynamoDB single-table design to CI/CD pipelines.

I recently learned about NestJS at work and the power of Inversion of Control and Dependency Injection. It just so happens that someone kindly added Dependency Injection to the framework that I use for my skill. Also, I’ve been having a difficult time getting some updates in the skill through certification, so it seemed like a good time to implement DI and write more some unit tests.

Issues with esbuild

Esbuild is a super fast typescript bundler. It gains this speed by completely ignoring the typescript. The problem with that is the Dependency Injection that I wanted to use relies on TypeScript Decorators. I had to turn on emitDecoratorMetadata and experimentalDecorators in my tsconfig file, but since esbuild just ignores that, all I get are errors.

Slowing Down esbuild

The solution was to get a plugin that negated all of the performance gained by ignoring TypeScript. The plugin runs each typescript file through tsc before passing it to esbuild.

First, install the esbuild-plugin-tsc package:

pnpm install -D esbuild-plugin-tsc

Now, import it and pass it into your esbuild configuration. I’m using esbuild with serverless stack, so the options are a little nested, but you can use plugins directly with esbuild as well.

import esbuildPluginTsc from 'esbuild-plugin-tsc';
...
const handler = new sst.Function(stack, 'Handler', {
  nodejs: {
    esbuild: {
      plugins: [
        esbuildPluginTsc({
          tsconfigPath: path.join(HANDLER_ROOT, 'tsconfig.json'),
        }),
      ],
    }
  }
});

Use the Right Format

In addition to using the plugin, I had to update my esbuild options to create commonjs-formatted output. While this was frustrating to figure out, it was pretty easy to implement. Just set the format setting to cjs.

In the serverless stack function, this is done in the nodejs configuration:

nodejs: {
  format: 'cjs',
  esbuild: {
    ...
  }
}

Summary

In this article, you’ve seen how to use decorators in a TypeScript project that’s bundled with esbuild. Hopefully, decorators will be standardized soon and we won’t have to worry about all of this. In the meantime, I hope this article saves a few people from the frustration of getting this to work!


Also published here.


Written by theBenForce | 5x AWS Certified Senior Software Engineer. I write about architecture, infrastructure as code, and automation.
Published by HackerNoon on 2023/05/13