A Rubyist explores Typescript, Node, and ESM
I recently had a small housekeeping task I needed to do which simply involved hitting JIRA’s REST API and creating some issues based on a template. Ruby tends to be my dynamically typed language of choice, with C# historically being my favorite statically typed language. This time though, I figured I’d give Typescript a shot. I had several goals in mind.
Understand the latest in Javascript modules
Since I last played with Javascript, there has been some progress in this area. ESM (ECMAScript Modules) exists and is now supported by most browsers. An NPM package contains one or more modules and a module exists to encapsulate code and avoid namespace pollution. This Redfin Engineering piece seems to do a better job than I could at explaining this. It’s also cool that, with the advent of HTTP 2, we can skip bundling entirely. That said, I noticed a few annoyances that are confusing as an outsider:
Too many ways/concepts to ‘export’ stuff - Default exports, named exports for example
Still caught halfway in between Common JS modules and ESM - A lot of packages out there are still CJS.
Some packages, like the openapi-typescript-fetch package I needed, are published with both, but the package was not published with the typical
cjs
vs.mjs
file convention, so it did not behave how Node (I used 17.6.0) expected it to. The variouspackage.json
changes (type: “module”, etc.) also seemed confusing. In this case, when I tried to use 3.x.x of node-fetch with my package usingtype: “module”
along with some other TS settings, I kept getting errors that “The requested module ‘openapi-typescript-fetch’ is a CommonJS module”, which is not technically true, but this was difficult to get around given I do not control that package.
Generated OAS/Swagger Typescript
The interfaces created by openapi-typescript seemed to accurately capture all of the details in Atlassian’s JIRA OAS spec. The fetch package above did prove somewhat useful and it was very easy to hand our generated code off to that fetcher. This is not too far off from the generated SOAP client code (AXIS, C# svcutil) or RAML Java generators.
The only real downside here was that the fetcher depends on the browser Fetch API that does not exist natively in NodeJS. While this is understandable (and solved by using the node-fetch package), it can still be awkward when crossing the browser/NodeJS divide.
1 import './fetch-polyfill' 2 import from "./jira" 3 import from "openapi-typescript-fetch" 4 import * as getBasicAuthHeader from 'basic-authorization-header' 5 6 const fetcher = Fetcher.for<paths>() 7 const env = process.env; 8 fetcher.configure({ 9 baseUrl: "https://stuff.atlassian.net", 10 init: { 11 headers: { 12 Authorization: getBasicAuthHeader(env.JIRA_USERNAME, 13 env.JIRA_API_KEY) 14 } 15 } 16 }); 17 18 (async () => { 19 const findProjects = fetcher.path('/rest/api/3/project') 20 .method('get') 21 .create() 22 let contentProjectId = (await findProjects({})).data.find(proj => { 23 return proj.key == 'BTC' 24 }).id 25 })(); 26
IDE support
I tend to use Jetbrains tooling so I decided to check out Webstorm. What was good:
Syntax completion with Typescript interfaces - This was helpful
Differentiation of generated code (JS) from source code (TS)
IDE respects same
tsconfig.json
thattsc
compiler uses for compilation
What could use improvement:
Building and running was more confusing. The IDE did not seem to add much value over just shelling with
npm run
. This is not that big of a deal.The ‘External libraries’ feature is a bit awkward since it does not show introspected
package.json
dependencies. This is also not a big deal sincenode_modules
mostly accomplishes that. In other languages where the derived set of packages does not live in a git ignored directory inside the project, ‘External libraries’ is more important than it is here.
Visual Studio Code is obviously a popular, if not the choice for most folks instead of Webstorm. I’ve just had too much comfort in the Jetbrains world to try it out.
Summary
All in all, the experience was OK. The module experience and errors that went with it were quite frustrating. Once that was dealt with, the Typescript, IDE, and generated Typescript code from OAS worked pretty well.