TypeScript is a fantastic tool for adding static typing to JavaScript, catching errors early and making large codebases more manageable. But let's be honest, sometimes a full TypeScript migration can feel like climbing Mount Everest. What if you could get most of the benefits of a statically typed system without the complete overhaul? Enter JSDoc, your secret weapon for adding types (and excellent documentation!) to your JavaScript projects.
The Power of JSDoc
JSDoc, those familiar comments above your code, can do more than explain what your functions do. They can also define types! Using @typedef
and @type
you can create complex, reusable type definitions in your JavaScript files. This allows you to add type safety and improve code readability without the full complexity of TypeScript.
Why JSDoc Might Be Right for You
- Smaller Projects: For smaller projects, the overhead of a full TypeScript setup might be overkill. JSDoc offers a lightweight way to add structure and documentation.
- Gradual Adoption: JSDoc can be a fantastic stepping stone towards TypeScript. You can start with JSDoc, generate declaration files, and gradually transition to full TypeScript if needed.
- Existing JavaScript Codebases: Migrating a massive JavaScript codebase to TypeScript can be daunting. JSDoc allows you to introduce types incrementally, without rewriting everything at once.
- Documentation as a First-Class Citizen: JSDoc emphasizes documentation, which is crucial regardless of your typing system. A well-documented codebase is always a plus.
Defining Types with @typedef
and @type
Here's where the magic happens. Let's see how you define types using JSDoc:
/**
* @typedef {Object} Point
* @property {number} x - The x-coordinate.
* @property {number} y - The y-coordinate.
*/
/**
* Calculates the distance between two points.
* @param {Point} p1 - The first point.
* @param {Point} p2 - The second point.
* @returns {number} The distance between the points.
*/
function distance(p1, p2) {
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
return Math.sqrt(dx ** 2 + dy ** 2);
}
/**
* @type {Point}
*/
const originPoint = { x: 0, y: 0 };
console.log(distance(originPoint, { x: 3, y: 4 }));
In this example:
-
@typedef {Object} Point
: Defines a Point type as an object with x and y properties, both numbers. -
@param {Point} p1
: Uses the Point type to specify the type of the p1 parameter. -
@param {Point} p2
: Uses the Point type to specify the type of the p2 parameter. -
@type {Point}
: Annotates the originPoint constant with the Point type.
Generating .d.ts
Files
The real power of JSDoc types comes when you generate TypeScript declaration files (.d.ts). The TypeScript compiler (tsc) can parse your JSDoc comments and create these files for you. This means you get type-checking and code completion in your IDE, even while writing plain JavaScript!
Here's the basic tsconfig.json
configuration:
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"outDir": "./dist",
"declaration": true,
"allowJs": true,
"emitDeclarationOnly": false, // Set to true to ONLY emit .d.ts files
"esModuleInterop": true
},
"include": ["./src/**/*.js"],
"exclude": ["node_modules"]
}
Then, run npx tsc
(or tsc
if installed globally) in your project directory. This will generate the .d.ts
files in your outDir (./dist in this example). Below you can see the declaration file tsc generates for you
The Best of Both Worlds
JSDoc with type generation provides a fantastic middle ground. You can enjoy the benefits of type safety and improved documentation without the full overhead of TypeScript. It's a great option for projects where a complete TypeScript migration isn't feasible or necessary, allowing you to gradually introduce types and improve your code quality over time.
So, next time you're thinking about adding types to your JavaScript, don't immediately jump to a full TypeScript conversion. Consider JSDoc – it might be exactly what you need.
And as always
Happy Coding 🧑🏻💻
Top comments (1)
Clean code man... If your functions need explanation you're doing it wrong.
Also if you add a tsconfig, you might as well use typescript 🤷 it's not the big hurdle you pretend it to be. If you have strict mode off, like in your tsconfig example, you can add or not add types whenever you like. It works literally exactly like your jsdoc examples, but without the ugly comment sections that pollute your code with redundant information or worse: add explanation to something that should have been self-explanatory.
The only time I would consider jsdoc is when you're maintaining a library and you want more human readable documentation.
Also: don't use a global version of tsc. Your code is written for a specific version of typescript and that version should be in the dev dependencies of your project.