rtyler

Exploring TypeScript

In my previous post I mentioned Jenkins Evergreen which requires a significant backend service to built and deployed to manage a pushed-update lifecycle. The prototype of that service which I wrote sometime late last year was in Ruby, but I quickly realized that my usual comfort area of Ruby and Python were not going to meet some requirements for the service. Consequently, I ventured into JavaScript, with the fantastic framework FeathersJS. Almost a year later, I now have some thoughts on the strengths and weaknesses of server-side JavaScript, and have in turn started to explore TypeScript. Overall I have found TypeScript to be interesting, but it is not without its weaknesses.

Complexity with External Libraries

When we first started building the Evergreen backend, Baptiste suggested TypeScript for adding helpful types and other features to our development of Evergreen. At the time, I was most concerned with the added complexity TypeScript might add to a project where nobody involved was familiar with TypeScript nor JavaScript. Having since started a couple of projects in TypeScript, I still believe this to be the case, unfortunately.

When adding other JavaScript-based libraries to a project, they often need a @types package or some other definition of what the types that package requires for the TypeScript compiler to do proper type checking. Microsoft does have this handy TypeSearch web site which makes discovering pre-existing types-packages easier. Without a types-package however, you may need to add type declarations into your own project for third-party libraries to realize benefits of TypeScript.

Assuming you understand the art and zen of TypeScript, creating these type declaration files isn’t too difficult, but for the novice to intermediate user, I believe it’s still overly complex.

Testing

For writing and executing JavaScript tests, I have been very happy with jest. TypeScript and Jest sort of get along together. I recommend the additional package ts-jest and the following configuration in package.json to ensure that TypeScript is being executed properly within tests.

  "jest": {
    "transform": {
      "^.+\\.tsx?$": "ts-jest"
    },
    "testRegex": "(/test/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx",
      "json",
      "node"
    ]
  }

The downside with ts-jest that I have found is that it’s not as comprehensive as the TypeScript compiler. This results in tests running despite compilation failures, which leads me to question what code is actually running under test. My workaround for this is to always maintain two terminal window panes open to the right of my editor. One with tsc -w, to constantly re-compile on filesystem changes, and jest --watchAll to constantly re-run tests on filesystem changes.

By running both of these together, I get the helpful compilation errors out of the TypeScript compiler, while still running my tests and getting the useful test feedback.

While not ideal, when tsc says I have compilation errors and jest passes my tests, I don’t get the most confidence in those green test results.

Outputting JavaScript

TypeScript out of the box seems to target a much newer version of ECMAScript than Node LTS (8) or latest (10) supports out of the box. For server-side applications, I’ve found the following tsconfig.json to result in good Node-compatible JavaScript:

{
  "compilerOptions": {
    "alwaysStrict" : true,
    "outDir": "./build",
    "module" : "commonjs",
    "skipLibCheck": true,
    "lib" : ["es2017"],
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "importHelpers" : true,
    "target": "es2015",
    "sourceMap": true
  },
  "include": [
      "./src/**/*"
  ]
}

Of particular note are the target and lib properties which ensure that some modern JavaScript features are made available with an appropriate output format which will run on Node 10. Now, what features those are specifically I can no longer remember as that was many Stack Overflow searches ago at this point.

As best as I can tell, tsc is rather smart in that if the configuration isn’t supposed to work with a certain version of JavaScript, than there will be compile-time errors for some standard API calls, such as methods added to Array or String in recent language revisions.

I’m not supremely comfortable cargo-culting configuration from one project to another, but I have little interest at this point in time in understanding the depths of how TypeScript is configured, and why one might choose various options depending on the project’s needs.


In my personal projects I have yet to truly realize the benefits of TypeScript but I have definitely been burned by the lack thereof in other projects. Experiencing bugs which would have been prevented in TypeScript gives me the confidence to continue the exploration of TypeScript, despite some of the challenges it presents.

In a future blog post, I hope to outline some of the challenges faced, and patterns needed for successfully using TypeScript together with a FeathersJS application. Honestly though, I’m not sure I’ve even figured that one out yet. And therein lies the fundamental problem with TypeScript:

It’s a great add-on to the JavaScript ecosystem, but it suffers the drawbacks of being an add-on.