Skip to content
Advertisement

How do I add TypeScript types to a JavaScript module without switching to TypeScript?

I guess the title is somewhat confusing, but I wasn’t able to put it in a more concise way in a short title. However, this is what I want to do:

  • I have an npm module that is written in JavaScript, not TypeScript.
  • Some of the users of this module are using TypeScript, so it would be nice if I could provide type annotations for my JavaScript code.
  • But I do not want to switch to TypeScript, but I want to stick with pure JavaScript.

So, what I am looking for is some kind of an external type declaration manifest, or something like that.

Is this possible? If so, how?

Please bear with me if this is a naive question to TypeScript users, but I have absolutely no experience with TypeScript, and hence I do not know what to search the web for. Maybe if someone can push me into the right direction by giving me a name for what I am looking for, I am able to find a solution on my own.

Advertisement

Answer

You will want to create Definition Files. These files are used to describe your JavaScript.

Let’s say for simplicity you have a function called add, it will take two numbers or an array of numbers.

/src/index.js

exports.add = function add(a, b) {
  if (Array.isArray(a)) return a.reduce((sum, val) => sum + val, 0)
  else if (arguments.length === 2 && typeof a === 'number' && typeof b === 'number') return a + b
  throw new Error('Invalid input') 
}

We can then create a definition file .d.ts, this will describe our JavaScript function. You should keep the file names and file structure the same as the JS within a root folder such as types.

/types/index.d.ts

/**
 * Sums up two numbers.
 *
 * @param {number} a The first number to add.
 * @param {number} b The second number to add.
 * @returns {number} The resulting sum of the two numbers.
 */
export declare function add(a: number, b: number): number
/**
 * Sums up a list of numbers.
 *
 * @param {number[]} numbers An array of numbers to add.
 * @returns {number} The resulting sum of all the numbers.
 */
export declare function add(numbers: number[]): number

Note: When creating overloads, the most complex definition should be first and the least complex definition should be last, see Overload Ordering and all the Do’s and Don’ts.

All that this file does is describe all of the functions/classes/interfaces/modules, etc. without any logic. Within the file you can create overloads as you can see from my example. This will allow you to define multiple ways that the function can be used. The file also supports JSDoc, so I would use it as well.

If you would like to split up a file into smaller files, you then need to include the file in the current file using a reference comment (it starts with three / and ends with />).

/// <reference path="./part-1.d.ts" />
/// <reference path="./part-2.d.ts" />

declare interface {
  someProperty: string;
}

Next, we need to modify the package.json, this will tell the TypeScript parser where to find the definitions for this package.

/package.json

{
  "types": "types/index.d.ts"
}

Note: The "typings" field is synonymous with "types", and could be used as well.

See Publishing for more information on package.json.

That is basically all there is to creating a .d.ts

Advertisement