Type-Check Plain JavaScript With JSDoc and @ts-check
Add // @ts-check to the top of a file and JSDoc types to your functions, and the TypeScript checker flags wrong arguments in your editor and in CI. No build step, no .ts files.
You don't need to rename every file to .ts and add a build step to get type checking. The TypeScript checker reads plain JavaScript, takes types from JSDoc comments, and reports wrong arguments, misspelled properties and missing null checks. Your code runs exactly as it does now. This takes about ten minutes to try on one file.
Step 1: install the checker
npm install --save-dev typescript
It's only used to check. Nothing it does ends up in your running code. The outputs below are from TypeScript 7.0.
Step 2: turn it on for one file
Add // @ts-check as the first line. In VS Code, errors start showing as red underlines right away. Then add JSDoc types to the function you care about:
// @ts-check
/**
* @typedef {object} CartItem
* @property {string} sku
* @property {number} price Price in centavos.
* @property {number} quantity
*/
/**
* @param {CartItem[]} items
* @param {number} [discountRate=0] 0.1 means 10% off.
* @returns {number}
*/
export function cartTotal(items, discountRate = 0) {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
return Math.round(subtotal * (1 - discountRate));
}
cartTotal([{ sku: "MOUSE-01", price: "450", quantity: 2 }]);
cartTotal([{ sku: "CABLE-02", price: 150, qty: 1 }]);
cartTotal([], "10%");
Check it from the command line:
npx tsc --noEmit --allowJs --target es2022 --module nodenext cart.js
cart.js(20,31): error TS2322: Type 'string' is not assignable to type 'number'.
cart.js(21,43): error TS2353: Object literal may only specify known properties, and 'qty' does not exist in type 'CartItem'.
cart.js(22,15): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
Three real bugs. Plain JavaScript runs all three lines without a single error and quietly returns wrong numbers.
Step 3: check the whole project
Create tsconfig.json in the project root:
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext"
},
"include": ["src/**/*.js"]
}
Now npx tsc checks every file under src, with no // @ts-check lines needed. strict turns on null checks, which find bugs like this one:
/** @type {Map<string, { id: string, total: number }>} */
const orders = new Map();
/** @param {string} id */
export function orderTotal(id) {
const order = orders.get(id);
return order.total;
}
src/orders.js(7,10): error TS18048: 'order' is possibly 'undefined'.
orders.get(id) returns undefined for an unknown id, and order.total would crash. Add if (!order) throw new Error("Unknown order " + id); before the return, and npx tsc exits with code 0.
💡 Tip: On an old project, strict: true can produce hundreds of errors at once. Start with "strict": false, fix what shows up, then add "strictNullChecks": true on its own. It finds the most real bugs.
Step 4: run it in CI
"scripts": {
"typecheck": "tsc",
"test": "vitest run"
}
Run npm run typecheck in your CI pipeline next to the tests. A pull request with type errors doesn't merge.
JSDoc cheat sheet
@param {string} name,@returns {Promise<Order[]>}- Optional with default:
@param {number} [limit=20] - Union:
@param {"paid" | "refunded"} status - Reuse a type:
@typedefonce, then{CartItem}anywhere in the file - From another file:
@param {import("./types.js").Order} order - Variable:
/** @type {Set<string>} */
âš Warning: Types are checked, not enforced. JSON from an API or values from a form can still be anything at runtime. Validate data where it enters your program.
When the checker is wrong
Put // @ts-expect-error with a reason on the line above. If the error later disappears, the comment itself becomes an error, so it never outlives the problem. Avoid // @ts-ignore, which stays silently forever.
Which function in your project gets called from the most places? Add // @ts-check to that file first, and count how many wrong calls it finds.
About
Turn on @ts-check, type functions and objects with JSDoc, run tsc --noEmit in CI and fix the first real errors. After this, wrong arguments get caught before you run anything.
- Responsible
- Mary Q
- Last Update
- 09/05/2026
- Reading Time
- 4 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.