SBN

Typing your JavaScript without writing TypeScript

We recently looked at how you can get some of the benefits of TypeScript in our JavaScript code base. If you've been through that process, then TypeScript is already helping to keep some type issues out of your JavaScript. But, because it is acting on JavaScript, TypeScript will not be able to infer as much as it would like to.

The good news is that we can give TypeScript more hints about the types that flow around our JavaScript application without changing the project to TypeScript.

Adding more types

One way to do this is by using JSDoc. JSDoc provides a way for you to document your code through comments that are also machine-readable. This was initially built to generate API documentation for your code, but TypeScript has adopted it as a valid way to provide type information in JavaScript files.

JSDoc

You can use JSDoc to set the types of variables. In this example, assigning an empty array to the variable names doesn't tell the type system anything, but if we use the JSDoc @type annotation, then we indicate to TypeScript that this variable should only be an array of strings. As this is documentation, it also tells you and your team what kind of variable this is.

/**
 * @type {Array}
 */
let names = [];

If you later try to assign a value with a different type to names or try to add an element of the wrong type, TypeScript will complain about it.

You can also define complex types with @typedef

/**
 * @typedef {object} TeamMember
 * @property {string} name - the full name of a team member
 * @property {Array} languages - the programming languages the member knows
 */

/**
 * @type {TeamMember}
 */
const teamMember = {
  name: "Phil Nash",
  languages: ["JavaScript", "TypeScript", "CSS", "HTML"],
};

With JSDoc annotations in place, you can import types from one file into another, meaning you don't have to repeat yourself and redefine types in multiple places.

You can also type the parameters and the return value of functions:

/**
 * @typedef {import("./teamMember").TeamMember} TeamMember
 * @param {Array} teamMembers - a list of team members
 * @returns {Array}
 */

function getNames(teamMembers) {
  return teamMembers.map(tm => tm.name);
}

There's more to JSDoc than this, and you can read up on the JSDoc annotations that TypeScript supports in the TypeScript documentation.

TypeScript declaration files

If you find writing type definitions in comments in your files too noisy, you can choose to write those definitions in a declaration file instead. TypeScript declaration files are the .d.ts files you'll find in the Definitely Typed project or in JavaScript libraries that also ship their type definitions.

Writing TypeScript declaration files is the closest this article will get to writing TypeScript itself. A declaration file declares the types of classes, functions and variables in TypeScript syntax without defining the contents.

To use a declaration file to define the TeamMember type, from above, you would create a teamMember.d.ts file and enter the following:

export type TeamMember = {
  name: string;
  languages: Array;
};

You can then import that in your JavaScript using the JSDoc import syntax:

/**
 * @type {import("./teamMember.d").TeamMember}
 */

const teamMember = {
  name: "John Doe",
  languages: ["JavaScript", "TypeScript", "HTML", "CSS"],
};

Importing your types from a declaration file keeps your type definitions separate from your code and gives you all of the expressiveness of the TypeScript type system. Adding declaration files like this to libraries that you write means that other projects can also install the library and access the types.

Still not TypeScript, just supercharged JavaScript

Remember, you're still in a JavaScript project here. You don't need to add these types, but if you find parts of your code that would benefit from them, the combination of JavaScript, JSDoc and, optionally, declaration files means that you can.

Giving more type information will help TypeScript understand your JavaScript better, which in turn helps your IDE to make better suggestions and helps SonarQube Server and SonarQube Cloud apply TypeScript rules to your source code.

Do watch out, though; after going through this evolution, from adding TypeScript to your JavaScript project and adding more types, you might want to convert your project all the way to TypeScript. Thankfully, if you have got this far, the effort to move is now much lower and, much like adding types to the code base, can be done incrementally.

*** This is a Security Bloggers Network syndicated blog from Blog RSS feed authored by Phil Nash. Read the original post at: https://www.sonarsource.com/blog/typing-javascript-without-typescript/