December 15, 2021

Type-safe JavaScript with JsDoc

JsDoc is a great tool for documenting code and providing type-safety in your JavaScript project without any additional configuration. Learn what JsDoc is, how it works, the benefits it provides, and how to use it in your project.

JavaScript is a powerful and flexible language that powers many applications on the internet. It is weakly typed and mostly assumes any type — pun intended. However, code editors such as VS Code create workflows that provide some level of type-safety while working with JavaScript.

The target of this article is JavaScript developers seeking to add type safety in their project and TypeScript developers as it explores code documentation with JsDoc. This article will discuss JsDoc - the benefits, how it works, inferring types in your code, and type inference with Prisma.

What is JsDoc?

JsDoc is an API documentation generator for JavaScript using multi-line comments. It scans through your code and generates documentation in available in a HTML website. JsDoc is compatible with both JavaScript and TypeScript.

Why use JsDoc?

Improves code documentation

Documenting code is a valuable skill for developers. Documentation provides information for yourself and other developers - by explaining what the code does and how it does it. This makes maintenance and code review simpler and reduces the entry barriers for other contributors to your code.

JsDoc improves code documentation in a more structured manner using tags to describe arguments, return values, namespaces, modules, etc.

Library authors take advantage of JsDoc to provide as much detail as possible, for example, on types, code examples/ mini-tutorials, and utility functions. An example of well-documented libraries are Nexus.

Nexus code documentationNexus code documentation

Provides type safety

A weakness JavaScript is known for is having a weak type system. JsDoc provides a way to infer types in your JavaScript project.

Having types improves code maintainability, speeds up refactoring, reduces guesswork trying to understand code, and could possibly save you hours of debugging typos, undeclared variables, and undefined types.

JsDoc paired with VS Code provides rich intellisense that boosts developer productivity.

Compatible with JavaScript and TypeScript

JsDoc is works seamlessly with both JavaScript and TypeScript. The types declared via JsDoc serve as a form of API documentation for your code. JsDoc can also be paired with TypeScript, making your code as descriptive as possible.

How does JsDoc work?

Under the hood of VS Code is the JavaScript Language Service that provides intellisense when working with JavaScript. The language service analyzes types from three different sources to provide great developer experience:

  • Implicitly inferring types from your code based on usage
  • TypeScript declaration files
  • JsDoc

VS Code ships with JsDoc support and therefore requires no set up to get started. You can configure VS Code to check types in your JavaScript project in the following ways:

  • Adding @ts-check at the top of every file.
  • Modifying the following setting: "js/ts.implicitProjectConfig.strictFunctionTypes": true
  • Using jsconfig.json or tsconfig.json by adding the "checkJs": true in the compilerOptions configuration
  • Explicitly specifying types using JsDoc

If you're working on VS Code, either of the would work just fine for any project. These options complement each other and it's up to you to choose what works better for you.

Fun fact: The former code name of the JavaScript Language Server was Salsa.

The fundamental element needed to document code using JsDoc is a tag. JsDoc provides two types of tags: block tags and inline tags.

Block tags provide means to annotate variables, functions, arguments, modules, namespaces, return types, and classes and describe what a specific code snippet does. These could be types, arguments, callbacks, modules, namespaces, etc. Inline tags are used to link to other parts of the documentation.

To get started with JsDoc, add an annotation before blocks of code. The annotations are captured between multi-line comments - /** */ - and use JsDoc's block tags, for example @type.

The annotations are picked up by the JavaScript Language service and infer types in your project. You can then preview the docs by hovering on the snippets of code, for example functions and variables. Besides documenting the code, JsDoc also adds type-safety to your application.

Example block tags that can be used to add types to your code are @type, @typedef, @params and @returns.

Working with types

JavaScript's primitive type types are supported in JsDocs. More advanced TypeScript types can be used in your project as well. Some of the example types this article will explore include:

Primitive types

/**
* @type {string}
*/
let firstName;

Structural types

/**
* @type {{name: string, age: number, isOk: boolean}}
*/
let user;

Nullable types

/**
* @type{?string} userName
*/
let userName;

Built-in objects

JsDoc allows you to reference the standard built-in objects in JavaScript such as Promise, Document, Window etc.

/**
* @type {Document}
*/
let document;

Union Types

/**
* @type {"asc"| "desc"} orderBy
*/
let orderBy;

Generic Types

/**
* @template D
* @param {D} data
* @return {D} data
*/
const createData = (data) => { return data };

Type inference with Prisma

Prisma is a database ORM that provides developers with a type-safe database client. Prisma provides an intuitive API that makes modelling and querying data fun for all developers. Currently, Prisma supports PostgreSQL, MySQL, SQLite, SQL Server and MongoDB preview.

Initialize your project

Create a project directory and navigate into it:

mkdir awesome-project # Give your project a memorable name
cd awesome-project
Copy

Next, install Prisma CLI as a development dependency and Prisma client:

npm init -y
npm install -D prisma
npm install @prisma/client
Copy

The Prisma CLI is as a development dependency used for the following:

  • Initializing Prisma in your project
  • Running database migrations
  • Generating Prisma Client
  • Managing the database schema and lifecycle
  • Introspecting your existing database.
  • Opening Prisma Studio - a database GUI for interacting with data

Prisma Client is the database client for querying data in your JavaScript or TypeScript application. Initialize Prisma in your project:

npx prisma init
Copy

This command does the following creates:

  • A folder called prisma at the root of your project
  • A .env file at the root of your project
  • A schema.prisma file. You will define your database models here.

Connect to your database

To connect to your database, set the url field in the datasource block in your Prisma schema to the connection URL pointing to your database. For simplicity, this guide will use SQLite.

datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
}
Copy

With your database models set up, run the command to create the database and the table as well.

npx prisma migrate dev --name init
Copy

This creates and applies a database migration against your database. A SQLite database is also created in the prisma directory. Prisma CLI generates the Prisma Client for you after a successful migration.

Assuming you have the following Prisma schema in your project:

datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
}
Copy

In a JavaScript file within your project add the following:

/**
* @typedef { import("@prisma/client").Post } Post
* @typedef { import("@prisma/client").Prisma.PostCreateInput } postCreateInput
*/
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient()
/**
* This function creates one post record
* @param {postCreateInput} data
* @returns {Promise<Post>} post
*/
const createOnePost = async (data) => {
return await prisma.post.create({
data
})
}
Copy

Hovering on the function declaration, function arguments, the data property will bring up the different types and descriptions referenced in the code. The types generated by the Prisma CLI are TypeScript types derived from your models. They are imported using the TypeScript syntax.

Explore the Prisma Client type-definitions by hovering on the imported type, for example PostCreateInput, pressing Ctrl or Cmd and left clicking on it.

Learn more about Productive Development With Prisma's Zero-Cost Type Safety and the Learn TypeScript: A Pocketguide Tutorial.

Conclusion

In summary, JsDoc makes a great tool for code documentation and provide a level of type safety when working with JavaScript. JsDoc also provides an incremental way of adopting types in your application without migrating your codebase to TypeScript.

If you have any questions regarding JsDoc, type safety, code documentation or Prisma, feel free to reach out to us on Twitter, ask a question on GitHub or send us a message on Slack.

Join the discussion

Follow @prisma on Twitter

Don’t miss the next post!

Sign up for the Prisma newsletter