bugl
bugl
HomeLearnPatternsPathsSearchPremium
HomeLearnPatternsPaths
Learn/TypeScript/TypeScript Core
TypeScript•TypeScript Core

TypeScript in JavaScript Projects (JSDoc)

JSDoc with TypeScript allows you to add type checking to JavaScript files without converting them to .ts .

This is perfect for gradual migration or when you want type safety in JavaScript projects.

Getting Started

To enable TypeScript checking in JavaScript files, you need to:

  • Create a tsconfig.json file (if you don't have one)
  • Enable checkJs or use // @ts-check in individual files

Example: JSDoc for Type Safety

// @ts-check /** * Adds two numbers. * @param {number} a * @param {number} b * @returns {number} */
function add(a, b) {
  return a + b;
}

Objects and Interfaces

Inline Object Types

// @ts-check /** * @param {{ firstName: string, lastName: string, age?: number }} person */
function greet(person) {
  return `Hello, ${person.firstName} ${person.lastName}`;
}
greet({ firstName: 'John', lastName: 'Doe' }); // OK
greet({ firstName: 'Jane' }); // Error: Property 'lastName' is missing

Using @typedef for Complex Types

// @ts-check /** * @typedef {Object} User * @property {number} id - The user ID * @property {string} username - The username * @property {string} [email] - Optional email address * @property {('admin'|'user'|'guest')} role - User role * @property {() => string} getFullName - Method that returns full name */
/** @type {User} */
const currentUser = {
  id: 1, username: 'johndoe', role: 'admin', getFullName() {
    return 'John Doe';
  }
};
// TypeScript will provide autocomplete for User properties console.log(currentUser.role);

Extending Types

// @ts-check /** @typedef {{ x: number, y: number }} Point */
/** * @typedef {Point & { z: number }} Point3D
*/
/** @type {Point3D} */
const point3d = { x: 1, y: 2, z: 3 };
// @ts-expect-error - missing z property
const point2d = { x: 1, y: 2 };

Function Types

Function Declarations

// @ts-check /** * Calculates the area of a rectangle * @param {number} width - The width of the rectangle * @param {number} height - The height of the rectangle * @returns {number} The calculated area */
function calculateArea(width, height) {
  return width * height;
}
// TypeScript knows the parameter and return types
const area = calculateArea(10, 20);

Function Expressions and Callbacks

// @ts-check /** * @callback StringProcessor * @param {string} input * @returns {string} */
/** * @type {StringProcessor}
*/
const toUpperCase = (str) => str.toUpperCase();
/** * @param {string[]} strings
* @param {StringProcessor} processor
* @returns {string[]}
*/
function processStrings(strings, processor) {
  return strings.map(processor);
}
const result = processStrings(['hello', 'world'], toUpperCase);
// result will be ['HELLO', 'WORLD']

Function Overloads

// @ts-check /** * @overload * @param {string} a * @param {string} b * @returns {string} */
/** * @overload
* @param {number} a
* @param {number} b
* @returns {number}
*/
/** * @param {string | number} a
* @param {string | number} b
* @returns {string | number}
*/
function add(a, b) {
  if (typeof a === 'string' || typeof b === 'string') {
    return String(a) + String(b);
  }
return a + b;
}
const strResult = add('Hello, ', 'World!'); // string
const numResult = add(10, 20);             // number

Advanced Types

Union and Intersection Types

// @ts-check /** @typedef {{ name: string, age: number }} Person */
/** @typedef {Person & { employeeId: string }} Employee */
/** @typedef {Person | { guestId: string, visitDate: Date }} Visitor */
/** @type {Employee} */
const employee = {
  name: 'Alice', age: 30, employeeId: 'E123'
};
/** @type {Visitor} */
const guest = {
  guestId: 'G456', visitDate: new Date()
};
/** * @param {Visitor} visitor
* @returns {string}
*/
function getVisitorId(visitor) {
  if ('guestId' in visitor) {
    return visitor.guestId;  // TypeScript knows this is a guest
  }
return visitor.name;      // TypeScript knows this is a Person
}

Mapped and Conditional Types

// @ts-check /** * @template T * @typedef {[K in keyof T]: T[K] extends Function ? K : never}[keyof T] MethodNames */
/** * @template T
* @typedef {{
    *   [K in keyof T as `get${'<' }Capitalize<string & K>{'>'}`]: () => T[K]
    * }} Getters
*/
/** @type {Getters<{ name: string, age: number }> } */
const userGetters = {
  getName: () => 'John', getAge: () => 30
};
// TypeScript enforces the return types
const name = userGetters.getName(); // string
const age = userGetters.getAge();   // number

Importing Types from Other Files

// @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 */

Creating Declaration Files

Create a types.d.ts file in your project:

// 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>;
}

Then use it in your JavaScript files:

// @ts-check
/** @type {import('my-module').Config} */
const config = {
 apiKey: '12345',
 timeout: 5000
};
// TypeScript will provide autocomplete and type checking
import { initialize } from 'my-module';
initialize(config);

Best Practices

Follow these best practices when using JSDoc with TypeScript:

  • Enable // @ts-check at the top of files where you want type checking
  • Use @typedef for complex types that are used in multiple places
  • Document all function parameters and return types
  • Use @template for generic functions and types
  • Create declaration files ( .d.ts ) for third-party libraries without types
  • Use @ts-expect-error instead of @ts-ignore when you expect an error

Common Pitfalls

Watch out for these common issues

  • Missing // @ts-check : Type checking won't work without it
  • Incorrect JSDoc syntax: A single typo can disable type checking
  • Type conflicts: When types from different sources don't match
  • Inference issues: Sometimes TypeScript can't infer types correctly
  • Performance: Large JavaScript files with complex types can be slow to check

Conclusion

Using JSDoc with TypeScript provides a powerful way to add type safety to your JavaScript projects without the need to convert files to TypeScript.

This approach is particularly useful for

  • Gradually migrating JavaScript codebases to TypeScript
  • Adding type checking to existing JavaScript projects
  • Working in environments where .ts files aren't supported
  • Documenting JavaScript code with type information

By following the patterns and best practices outlined in this tutorial, you can enjoy many of the benefits of TypeScript while continuing to work with JavaScript.

Remember

While JSDoc provides excellent type checking, for new projects or complete migrations, consider using .ts files for the best TypeScript experience.

Ready to try TypeScript with JSDoc?

Start by adding // @ts-check to your JavaScript files and gradually add type annotations using JSDoc.

The TypeScript compiler will help you catch errors before they reach production!

Previous

TypeScript Decorators

Next

TypeScript Migration Guide

This chapter

Overview
40

Lessons

241m

Read time

1. TypeScript Tutorial2. TypeScript Introduction3. TypeScript Getting Started4. TypeScript Simple Types5. TypeScript Explicit Types and Inference6. TypeScript Special Types7. TypeScript Arrays8. TypeScript Tuples9. TypeScript Object Types10. TypeScript Enums11. TypeScript Type Aliases and Interfaces12. TypeScript Union Types13. TypeScript Functions14. TypeScript Casting15. TypeScript Classes16. TypeScript Basic Generics17. TypeScript Utility Types18. TypeScript Keyof19. TypeScript Null & Undefined20. TypeScript Definitely Typed21. TypeScript 5.x Updates22. TypeScript Configuration23. TypeScript with Node.js24. TypeScript with React25. TypeScript Tooling26. TypeScript Advanced Types27. TypeScript Type Guards28. TypeScript Conditional Types29. TypeScript Mapped Types30. TypeScript Type Inference31. TypeScript Literal Types32. TypeScript Namespaces33. TypeScript Index Signatures34. TypeScript Declaration Merging35. TypeScript Async Programming36. TypeScript Decorators37. TypeScript in JavaScript Projects (JSDoc)38. TypeScript Migration Guide39. TypeScript Error Handling40. TypeScript Best Practices

On this page

Getting StartedObjects and InterfacesFunction TypesAdvanced TypesImporting Types from Other FilesCreating Declaration FilesBest PracticesCommon PitfallsConclusionReady to try TypeScript with JSDoc?