Typing a JavaScript Library

An Untyped Library

Suppose your company has an old internal JavaScript package @shopease/pricing:

// node_modules/@shopease/pricing/index.js
exports.applyCoupon = function (amount, coupon) { /* ... */ };
exports.formatInr = function (amount, opts) { /* ... */ };
exports.VERSION = "1.3.0";

Write the Declarations

Create types/shopease-pricing.d.ts (and make sure the folder is included in tsconfig.json):

declare module "@shopease/pricing" {
  export interface Coupon {
    code: string;
    percent?: number;
    flat?: number;
    minOrder?: number;
  }

  export interface FormatOptions {
    decimals?: 0 | 2;
    symbol?: boolean;
  }

  export function applyCoupon(amount: number, coupon: Coupon): number;
  export function formatInr(amount: number, opts?: FormatOptions): string;
  export const VERSION: string;
}

Now imports are fully typed:

import { applyCoupon, formatInr } from "@shopease/pricing";

const total = applyCoupon(1500, { code: "FEST20", percent: 20 });
console.log(formatInr(total, { decimals: 2 }));

JSDoc Types in JavaScript

Another option during migration: keep the .js file, add JSDoc types, and enable "allowJs": true, "checkJs": true. TypeScript reads the comments as types and can generate .d.ts from them.

/**
 * @param {number} amount
 * @param {{ code: string, percent?: number }} coupon
 * @returns {number}
 */
function applyCoupon(amount, coupon) {
  return amount - (amount * (coupon.percent ?? 0)) / 100;
}