The Strengths and Weaknesses
November 5, 2024 ยท 5 min read
When beginning a new JavaScript project, type safety always comes up as a critical consideration. The two main contenders - TypeScript and JSDoc - each offer distinct approaches to adding types to JavaScript. Let's dive deep into their tradeoffs and see how they actually work in production.
TypeScript's appeal goes beyond its type system. While "TypeScript is a typed superset of JavaScript" is the common tagline, its powerful features make it a compelling choice for complex applications:
// TypeScript's type inference is surprisingly smart
const nums = [1, 2, 3]; // Type: number[]
const first = nums[0]; // Type: number
// Structural typing allows for flexible interfaces
interface User {
id: number;
name: string;
email?: string; // Optional properties
}
// TypeScript understands this satisfies User
const user = {
id: 1,
name: "Sam",
website: "https://example.com", // Extra properties are fine
};
// Union types enable precise modeling
type Status = "draft" | "published" | "archived";
Project-Wide Refactoring
Rich IDE Integration
Type Inference
JSDoc takes a different approach - it adds type information through comments, making it a zero-build-step solution:
/**
* @typedef {Object} User
* @property {number} id
* @property {string} name
* @property {string} [email]
*/
/**
* Creates a new user
* @param {string} name
* @param {string} [email]
* @returns {User}
*/
function createUser(name, email) {
return {
id: Date.now(),
name,
...(email && { email }),
};
}
No Build Step
Gradual Adoption
Built-in Documentation
The choice between TypeScript and JSDoc often comes down to practical factors:
You can actually leverage both - TypeScript understands JSDoc annotations perfectly. This means you can:
Here's what that looks like:
// Using JSDoc with TypeScript
/** @type {Array<number>} */
const numbers = [1, 2, 3];
/**
* A user in our system
* @typedef {Object} User
*/
interface User {
id: number;
name: string;
}
One often overlooked aspect is the impact on development and build performance:
// @ts-check is per-fileLet's look at some more complex patterns in both systems:
// TypeScript's advanced type features
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue };
// Utility types
type Partial<T> = { [P in keyof T]?: T[P] };
type Required<T> = { [P in keyof T]-?: T[P] };
// Template literal types
type Color = "red" | "blue";
type Size = "small" | "large";
type Product = `${Color}-${Size}-item`; // "red-small-item" | "red-large-item" | ...
And the JSDoc equivalent:
/**
* @typedef {string|number|boolean|null|Array<JSONValue>|Object<string, JSONValue>} JSONValue
*/
/**
* @template T
* @typedef {Object} PartialType
* @property {T[keyof T]} [key]
*/
/**
* @typedef {"red"|"blue"} Color
* @typedef {"small"|"large"} Size
* @typedef {`${Color}-${Size}-item`} Product
*/
Both TypeScript and JSDoc are battle-tested solutions for adding type safety to JavaScript. TypeScript offers a more comprehensive solution with advanced language features, while JSDoc provides a lightweight approach that's perfect for gradual adoption.
The key is understanding your project's needs:
Remember, the goal is to write maintainable code that helps your team move faster - choose the tool that best serves that purpose.