Unless you’ve been hiding under a rock the last several years (and let’s face it, hiding under a rock sometimes feels like the right thing to do), you’ve probably heard of and likely used TypeScript. TypeScript is a syntactical superset of JavaScript that adds — as its name suggests — typing to the web’s favorite scripting language.
TypeScript is incredibly powerful, but is often difficult to read for beginners and carries the overhead of needing a compilation step before it can run in a browser due to the extra syntax that isn’t valid JavaScript. For many projects this isn’t a problem, but for others this might get in the way of getting work done. Fortunately the TypeScript team has enabled a way to type check vanilla JavaScript using JSDoc.
Setting up a new project
To get TypeScript up and running in a new project, you’ll need NodeJS and npm. Let’s start by creating a new project and running npm init. For the purposes of this article, we are going to be using VShttps://code.visualstudio.comCode as our code editor. Once everything is set up, we’ll need to install TypeScript:
npm i -D typescript
Once that install is done, we need to tell TypeScript what to do with our code, so let’s create a new file called tsconfig.json and add this:
{
compilerOptions: {
target: esnext,
module: esnext,
moduleResolution: node,
lib: [es2017, dom],
allowJs: true,
checkJs: true,
noEmit: true,
strict: false,
noImplicitThis: true,
alwaysStrict: true,
esModuleInterop: true
},
include: [ script, test ],
exclude: [ node_modules ]
}
For our purposes, the important lines of this config file are the allowJs and checkJs options, which are both set to true. These tell TypeScript that we want it to evaluate our JavaScript code. We’ve also told TypeScript to check all files inside of a /script directory, so let’s create that and a new file in it called index.js.
A simple example
Inside our newly-created JavaScript file, let’s make a simple addition function that takes two parameters and adds them together:
function add(x, y) {
return x + y;
}
Fairly simple, right? add(4, 2) will return 6, but because JavaScript is dynamically-typed you could also call add with a string and a number and get some potentially unexpected results:
add(4, 2); // returns 42
That’s less than ideal. Fortunately, we can add some JSDoc annotations to our function to tell users how we expect it to work:
/**
* Add two numbers together
* @param {number} x
* @param {number} y
* @return {number}
*/
function add(x, y) {
return x + y;
}
We’ve changed nothing about our code; we’ve simply added a comment to tell users how the function is meant to be used and what value should be expected to return. We’ve done this by utilizing JSDoc’s @param and @return annotations with types set in curly braces ({}).
Trying to run our incorrect snippet from before throws an error in VS Code: TypeScript evaluates that a call to add is incorrect if one of the arguments is a string.
In the example above, TypeScript is reading our comment and checking it for us. In actual TypeScript, our function now is equivalent of writing:
/**
* Add two numbers together
*/
function add(x: number, y: number): number {
return x + y;
}
Just like we used the number type, we have access to dozens of built-in types with JSDoc, including string, object, Array as well as plenty of others, like HTMLElement, MutationRecord and more.
One added benefit of using JSDoc annotations over TypeScript’s proprietary syntax is that it provides developers an opportunity to provide additional metadata around arguments or type definitions by providing those inline (hopefully encouraging positive habits of self-documenting our code).
We can also tell TypeScript that instances of certain objects might have expectations. A WeakMap, for instance, is a built-in JavaScript object that creates a mapping between any object and any other piece of data. This second piece of data can be anything by default, but if we want our WeakMap instance to only take a string as the value, we can tell TypeScript what we want:
/** @type {WeakMap