Skip to main content

TypeScript in JavaScript Projects (JSDoc)

JSDoc comments let you add TypeScript type checking to plain JavaScript files without renaming them to .ts.

This is ideal when you want to migrate a project step by step, or when you simply want type safety in a JavaScript codebase.

Note: the examples on this page are written as JavaScript with JSDoc types. Save them as .js files with // @ts-check at the top (or checkJs enabled) to see the type checking described. In a .ts file the compiler ignores JSDoc types and relies on the regular annotations instead.


Getting Started

To make TypeScript check your JavaScript files you need to:

  1. Create a tsconfig.json file (if you do not have one)
  2. Enable checkJs in it, or put // @ts-check at the top of the individual files you want checked

Example: JSDoc for Type Safety

A JSDoc comment above the function tells TypeScript that both parameters and the return value are numbers.

Prompt
Show the smallest JSDoc-typed function with // @ts-check enabled at the top of the file. Write an add(a, b) function documented with @param {number} a, @param {number} b and @returns {number} that returns the sum. Only the declaration is needed — the program prints nothing.
Output: TypeScript file and captured output

Objects and Interfaces

You can describe object shapes directly in a JSDoc tag, or give them a name with @typedef so the type can be reused in many places.

Inline Object Types

The parameter type is written inline as an object literal type. In a checked .js file, calling greet without lastName is reported as an error.

Prompt
Show an inline object type written in JSDoc for a function parameter. With // @ts-check on, document greet(person) with @param {{ firstName: string, lastName: string, age?: number }} person and return Hello, <firstName> <lastName>. Call it once with a complete object (John Doe) and once with only firstName: "Jane", marking the second call with a comment as the missing-lastName error a checked .js file reports. Nothing is printed.
Output: TypeScript file and captured output

Using @typedef for Complex Types

@typedef plus @property lines define a named User type. Square brackets mark an optional property, and a parenthesised union restricts role to a few values.

Prompt
Show @typedef and @property tags describing an object type in JSDoc. Define a User typedef with id: number, username: string, optional email, a role limited to "admin" | "user" | "guest", and a getFullName method returning a string. Declare currentUser with /** @type {User} */ (id 1, username johndoe, role admin, getFullName() returning John Doe) and print currentUser.role.
Output: TypeScript file and captured output

Extending Types

An intersection (Point & { z: number }) builds a new type on top of an existing one. In a checked .js file the object without z is an error, which the @ts-expect-error comment expects. Compiled as a .ts file the JSDoc types are ignored, so the compiler reports the directive as unused instead.

Prompt
Show how one JSDoc typedef can extend another with an intersection type. Define Point as { x: number, y: number } and Point3D as Point & { z: number }. Declare point3d typed as Point3D with x 1, y 2, z 3, then a point2d object without z preceded by a // @ts-expect-error - missing z property comment. In a .js file with // @ts-check the missing z is the expected error; in a .ts file JSDoc types are ignored, so the compiler reports the directive as unused instead.
Output: TypeScript file and captured output

Function Types

JSDoc can describe every part of a function: its parameters, its return value, callback types and even several overloaded signatures.

Function Declarations

Each @param gives a parameter a type and a short description; @returns does the same for the result.

Prompt
Show a fully documented function declaration with JSDoc parameter and return types. With // @ts-check on, write calculateArea(width, height) documented with @param {number} for both parameters (with short descriptions) and @returns {number} The calculated area, returning the product. Store calculateArea(10, 20) in area. Nothing is printed.
Output: TypeScript file and captured output

Function Expressions and Callbacks

@callback defines a reusable function type. It can then be used with @type on an arrow function or as a parameter type.

Prompt
Show @callback for typing function expressions and callback parameters in JSDoc. Define a StringProcessor callback type taking a string and returning a string. Declare toUpperCase with @type {StringProcessor} as an arrow function, then processStrings(strings, processor) documented with @param {string[]}, @param {StringProcessor} and @returns {string[]} that maps the array through the processor. Call it with ["hello", "world"] and toUpperCase. Nothing is printed.
Output: TypeScript file and captured output

Function Overloads

Several @overload blocks give one function more than one signature. The last block describes the implementation that handles all of them.

Prompt
Show JSDoc @overload tags giving one function two typed signatures. Write add(a, b) with two @overload blocks — string plus string returns string, number plus number returns number — followed by the implementation signature string | number. If either argument is a string, return the concatenation via String(), otherwise the numeric sum. Call it as add("Hello, ", "World!") and add(10, 20). Nothing is printed.
Output: TypeScript file and captured output

Advanced Types

More advanced TypeScript features — unions, intersections, generics, mapped and conditional types — are also available through JSDoc.

Union and Intersection Types

& combines types and | allows one of several types. An in check narrows a union inside a function.

Prompt
Show union and intersection types built with JSDoc @typedef. Define Person (name, age), Employee as Person & { employeeId: string } and Visitor as Person | { guestId: string, visitDate: Date }. Declare an employee (Alice, 30, E123) and a guest (G456, new Date()), then getVisitorId(visitor) that uses an "guestId" in visitor check to return either guestId or name. Nothing is printed.
Output: TypeScript file and captured output

Mapped and Conditional Types

@template makes a typedef generic. The typedef body can then use mapped types, key remapping and conditional types just like a .ts file.

Prompt
Show that JSDoc @template typedefs can express mapped and conditional types. Define a generic MethodNames<T> typedef that keeps only the keys of T whose values are functions, and a generic Getters<T> mapped type that renames each key K to `get${Capitalize<K>}` returning () => T[K]. Declare userGetters typed as Getters<{ name: string, age: number }> with getName returning "John" and getAge returning 30, and read both into name and age. Add export {} so name does not clash with the global. Nothing is printed.
Output: TypeScript file and captured output

Type Imports

A JSDoc type can be imported from another file or from a package with import('...') inside a @typedef. For libraries that ship no types, you can write your own declaration file (.d.ts).

Importing types in JSDoc

Three ways to bring a type into a JavaScript file: from a local TypeScript file, from a package in node_modules, and with a new name.

script.ts
// @ts-check

// Importing types from TypeScript files
/** @typedef {import('./types').User} User */

// Importing types from node_modules
/** @typedef {import('express').Request} ExpressRequest */

// Importing with renaming
/** @typedef {import('./api').default as ApiClient} ApiClient */

Declaration file for a module

A types.d.ts file that declares the my-module package: a Config interface and two exported functions.

script.ts
// types.d.ts
declare module 'my-module' {
  export interface Config {
    apiKey: string;
    timeout?: number;
    retries?: number;
  }

  export function initialize(config: Config): void;
  export function fetchData<T = any>(url: string): Promise<T>;
}

Best Practices

Follow these practices when you use JSDoc with TypeScript:

  • Put // @ts-check at the top of every file you want checked
  • Use @typedef for complex types that appear in more than one place
  • Document all function parameters and return types
  • Use @template for generic functions and types
  • Write declaration files (.d.ts) for third-party libraries without types
  • Prefer @ts-expect-error over @ts-ignore when you expect an error

Common Pitfalls

Watch out for these common issues:

  • Missing // @ts-check: without it (or checkJs) nothing is checked
  • Incorrect JSDoc syntax: a single typo can switch off type checking for that comment
  • Type conflicts: types from different sources may not match
  • Inference issues: sometimes TypeScript cannot work out a type on its own
  • Performance: large JavaScript files with complex types can be slow to check

Conclusion

JSDoc with TypeScript is a powerful way to add type safety to a JavaScript project without converting files to TypeScript.

It is especially useful for:

  • Gradually migrating a JavaScript codebase to TypeScript
  • Adding type checking to an existing JavaScript project
  • Working in environments where .ts files are not supported
  • Documenting JavaScript code with type information

With the patterns on this page you get many of TypeScript's benefits while still writing JavaScript.

Remember: for new projects or complete migrations, .ts files still give the best TypeScript experience.

Ready to start? Add // @ts-check to your JavaScript files and start adding JSDoc type annotations. The compiler will catch errors before they reach production.