How to Abuse TypeScript Definitions for Better Code Assistance in JavaScript Projects

So there I was, writing a CLI tool in plain old JavaScript. I had pulled in yargs, as I often do.

Here's the source of my executable:

screenshot

I'm looking at the above and wondering why demandCommand is highlighted as an instance member function, but usage, command, help etc. are not.

I note that when typing the call to usage(), I am not offered any information about the parameters or the types thereof.

WebStorm & TypeScript

Some time ago, I had noticed WebStorm will pull in type definitions from 3rd-party libraries which ship with TypeScript definitions. WebStorm will use those definitions to improve highlighting, provide type hints, inline docs, better refactoring, etc--even if I'm not actually using TypeScript in my project.

One day, I decided to see what would happen if I simply added @types/yargs as a dev dependency:

$ npm i @types/yargs -D

I ran the above, then reloaded my source. Imagine my surprise when I saw:

236ECEF6-31DE-497A-82FA-FDD6A9460A8B

Huh. What if...

46B5AFB8-F713-4BCE-A9D8-6526ACFE485B

Wow.

That's Cool, But...

... but I don't really want to install a bunch of @types/* as development dependencies. The modules aren't particularly heavy. Yet, stuffing this in the package.json of a pure JavaScript project:

{
  "devDependencies": {
    "@types/yargs": "^8.0.2"
  }
}

...provides no functional value whatsoever.

Furthermore, even if this works in WebStorm, I dunno if it works in VS Code, Atom, Sublime, etc. I don't expect contributors to use the same IDE or editor I do. A dotfile is one thing, but we're talkin' 'bout a dependency.


External library—get it? Laëtitia Buscaylet / Unsplash

The Hack: External Libraries

WebStorm allows you to add arbitrary "external libraries" to your project.
This strategy is how WebStorm manages to provide code assistance for Node.js core modules, ES5, ES2015+, the DOM API, WebGL, etc. These are enabled by default, as well as the local node_modules/ dir.

If you reference a lib in a <script> tag which points to a CDN, WebStorm will complain that it doesn't know anything about that script; it prompts you to download the script and add it as an external library.

But this can point to a directory or file on disk. So I removed @types/yargs from my project, and installed it globally:

$ npm i -g @types/yargs

I then went into the "external libraries" settings of my project in WebStorm, and added the destination directory (for me, this was /usr/local/lib/node_modules/@types/yargs; yours may be different). I marked this library as "global", so that I could use it in any project I open in WebStorm.

I then configured my project to actually use the library (via something called "Manage Scopes", which isn't the same thing as the other "Scopes" in WebStorm).

Sure enough, this enabled the extra code assistance—even if @types/yargs was no longer a dev dependency.

Making It Suck Less

This was dope and sick, but it also meant I would need to install the definitions I wanted manually, and then keep them up-to-date, which was not dope nor sick.

I knew that all of the type definitions live in a single repo.

I tried to install that globally, but there is no definitely-typed package (see for yourself). I tried to install the Git repo globally, which worked:

$ npm install --global \
    https://github.com/DefinitelyTyped/DefinitelyTyped.git

But upon closer inspection of /usr/local/lib/node_modules/definitely-typed, it was not actually a Git working copy. This means git pull wouldn't just work.

Still, if I had a working copy, I could just symlink its types dir to /usr/local/lib/node_modules/@types...

The all-types Package

...which is exactly what I automated with all-types.

The README has detailed instructions about how to install, set things up in WebStorm, and keep the install up-to-date.

If you pay attention to nothing else in the README, I suggest that you don't add all of DefinitelyTyped at once. Not because of performance--rather, it just adds a lot of noise to the code assistance environment.

I'm interested in making this easily consumable in other editors & IDEs, if possible. If you find your editor of choice can do this, please contribute with instructions!

Something for Nothing?

Let's agree that a great thing about TypeScript is the code assistance it provides to editors and IDEs. Even if you abhor TypeScript, that's tough to argue with. But what if you got that for free?

This hack provides at least some of the value from the TypeScript ecosystem without having to commit to TypeScript.

Yet, the most obvious deficiency is that unless your own code is written in TypeScript, this is limited. But it's much, much better than nothing.

It's worth mentioning that editors & IDEs are free (as in beer). Do they work with all-types? I have no idea. Clue me in!

Title image by Windell Oskay; part of a series of LEGO Abominations