Recipes
Recipes for typing real JavaScript
Each recipe starts from a concrete question about real code, answers it up front, then shows the TypeScript spelling of the same thing and the mistakes tsc catches. Every example on a recipe page is a real file checked with tsc when the site is built.
The recipes are listed in six groups: functions, objects and data, classes, modules and packages, Node.js and the browser, and frameworks and tests.
Functions
Parameters that are optional, rest or callbacks, overloads and generic helpers, this, and what async functions and generators return.
-
How do I mark a parameter as optional in JSDoc?
Some of my function's parameters can be left out, and some have default values. How do I write that in JSDoc so callers may skip them and tsc still checks what they pass?
- @param
- Optional parameters
- Default values
-
How do I type rest parameters (...args) in JSDoc?
My function takes any number of arguments with ...quantities. What do I write in the @param tag, and why does spreading an array into a normal function fail?
- @param
- Rest parameters
- Spread
-
How do I type a callback parameter in JSDoc?
My function takes a callback. How do I say what arguments the callback gets and what it must return, so the code passed in is checked too?
- @callback
- @param
- Function types
-
How do I write function overloads in JavaScript?
My function returns one product for a string and a list for an array. With a union return type every caller has to narrow the result. How do I give each kind of call its own return type in JavaScript?
- @overload
- Overloads
- TypeScript 5.0
-
How do I write a generic function in JavaScript with @template?
I have small helpers like last(), indexById() and pluck() that work on any array. With @param {any[]} they lose the element type. How do I make them generic in JSDoc?
- @template
- Generics
- keyof
-
How do I type "this" inside a JavaScript function?
My event handler is a normal function that uses this, and tsc says 'this' implicitly has type 'any'. How do I say what this is in JSDoc?
- @this
- this
- noImplicitThis
-
How do I type a function that also has properties?
My function also carries a few fields, like formatPrice.symbol and a call counter. How do I get tsc to know about those properties without turning the function into a class?
- Function properties
- @typedef
- @type
-
How do I type what an async function returns in JSDoc?
My async function returns an order. Do I write @returns {Order} or @returns {Promise<Order>}, and why does tsc say the property does not exist when I use the result?
- @returns
- Promise
- async
-
How do I type the results of Promise.all in JavaScript?
I load a customer and their orders in parallel with Promise.all. Sometimes the destructured results have the right types and sometimes they are a union of both. What decides that?
- Promise.all
- Tuples
- Promise.allSettled
-
How do I type an async generator in JSDoc?
I page through an API with an async generator function and loop over it with for await. What do I put in @returns, and does the @yields tag do anything?
- AsyncGenerator
- for await
- @returns
Objects and data
Options objects, config objects, keys and records, enums without enum, unions you narrow, guards and assertions, maps, sets and tuples.
-
How do I type an options object with default values in JSDoc?
My function takes an options object where every property is optional and has a default. How do I write the type once, apply the defaults, and still get each option checked?
- @typedef
- @property
- Options objects
- Spread
-
How do I check a config object without losing its literal types?
When I annotate my config object with @type, I lose the exact keys and values: every route name is allowed and logLevel is just a union. How do I check the object and keep what it actually holds?
- @satisfies
- Config objects
- TypeScript 5.0
-
Why does Object.keys return string[], and how do I keep the key type?
I loop over Object.keys(stock) and then read stock[item], and tsc says the element implicitly has an 'any' type. The keys come from the object itself. Why are they plain strings?
- Object.keys
- keyof
- Type casts
-
How do I type the result of JSON.parse in JavaScript?
JSON.parse gives me any, so a typo in a property name or a wrong type in the file goes unnoticed. How do I give the parsed value a type in JSDoc, and when is a cast not enough?
- JSON.parse
- unknown
- @type
-
How do I type an object whose keys are a fixed set of strings?
I keep a label for every order status in an object. When a new status is added, I want tsc to tell me the object is missing it. How do I type the keys as that union in JSDoc?
- Record
- Literal unions
- Partial
-
How do I write an enum in JavaScript with JSDoc?
In TypeScript I would write enum OrderStatus. My code is plain JavaScript. How do I get a fixed set of named values that tsc checks, and what is wrong with the @enum tag?
- Enums
- @typedef
- Object.freeze
- @enum
-
How do I narrow a union of object types in JavaScript?
A payment can be a card, an invoice or a voucher, each with different fields. How do I describe that in JSDoc so that each branch of my switch knows which fields exist, and so a new kind cannot be forgotten?
- Discriminated unions
- @typedef
- never
-
How do I write a type guard function in JSDoc?
I wrote isProduct(value) to check unknown rows, but after if (isProduct(row)) tsc still treats row as unknown. How do I make my check function narrow the type?
- Type guards
- @returns
- unknown
-
How do I write an assertion function in JSDoc?
I have an assert(condition, message) helper that throws, but tsc does not know that the code after it can rely on the condition. How do I tell it, in JSDoc?
- Assertion functions
- @returns
- asserts
-
How do I type a reducer and its actions in JavaScript?
My cart state is updated by a reducer that switches on action.type. How do I type the state and the actions in JSDoc so each case knows its payload, and a dispatch with a wrong action or field is caught?
- Reducers
- Discriminated unions
- @typedef
-
How do I type a Map or a Set in JSDoc?
I keep products in a Map keyed by SKU, but tsc lets me put anything in it, and when I add a type it complains that get() might be undefined. How do I type Maps and Sets properly in JSDoc?
- Map
- Set
- @type
-
How do I return a tuple from a JavaScript function?
My function returns [found, quantity], and after destructuring both variables are boolean | number. How do I tell tsc that the first element is a boolean and the second a number?
- Tuples
- @returns
- Const assertions
Classes
Fields and constructors, private members, accessors, static members, subclasses, mixins and interfaces a class promises to implement.
-
How do I type class fields and the constructor in JavaScript?
My JavaScript classes have fields declared at the top and others set in the constructor. Where does the JSDoc go, and why does tsc complain about an empty array field or a field that starts as null?
- Classes
- @type
- @param
- strictPropertyInitialization
-
Should I use #private fields or @private in JavaScript?
I want a class field that other code cannot touch. JavaScript has #fields, and JSDoc has @private. Which should I use in a checked .js file, and what does each one actually stop?
- #private
- @private
- Classes
-
How do I type getters and setters in a JavaScript class?
My Product class has a price getter and a setter that also accepts a string like "39.00". Where do the JSDoc types go on accessors, and how do I keep the getter returning a number?
- Getters
- Setters
- Classes
-
How do I type static properties and methods in a JavaScript class?
My Order class has a tax rate shared by every order and a static fromLines() factory. Do static members need JSDoc of their own, and does the @static tag do anything?
- static
- Classes
- @static
-
How do I type a subclass in JavaScript with @extends and @override?
I have a base Notifier class and an EmailNotifier that extends it. Do I need @extends in JSDoc, how do I mark an overridden method, and what happens when the override does not match the base?
- @extends
- @override
- noImplicitOverride
-
How do I type a mixin in JavaScript?
I add timestamps to several classes with a function that takes a class and returns a subclass. How do I type that function in JSDoc so the result has both the base class's members and the new ones?
- Mixins
- @template
- Constructor types
-
How do I make a JavaScript class implement an interface?
Several classes in my app store orders: one in memory for tests, one in a database. I want tsc to check that each one has the methods the rest of the code calls. How do I declare that in JavaScript?
- @implements
- @typedef
- Classes
Modules and packages
Exports in CommonJS and ES modules, sharing types between files, dynamic imports, packages without types, globals, environment variables and JSON files.
-
How do I type module.exports and require() in CommonJS JavaScript?
My Node.js project still uses require() and module.exports. Does JSDoc type checking work there the way it does with import and export, and how do I share a @typedef between CommonJS files?
- CommonJS
- module.exports
- require
- ES modules
-
How do I import and re-export types between JavaScript files?
I define Order and LineItem with @typedef in one file. How do I use them in other files, and how do I collect the types of several modules in one place for the rest of the app to import?
- @import
- @typedef
- import() types
- TypeScript 5.5
-
How do I type a dynamic import() in JavaScript?
I load a charting module only when a report needs it, with await import(). Is the result typed, and how do I type a variable that holds the module once it is loaded?
- import()
- typeof import
- Lazy loading
-
How do I add types for an npm package that has none?
I import a small npm package that has no types and no @types package. tsc says it could not find a declaration file. How do I give it types from my own project?
- declare module
- .d.ts
- TS7016
-
How do I declare a global variable or add a property to window?
My page script uses window.dataLayer from an analytics snippet and an __APP_VERSION__ constant my bundler injects. tsc says neither exists. Where do I declare them?
- declare global
- Window
- .d.ts
-
How do I type process.env variables in JavaScript?
tsc says process.env.DATABASE_URL might be undefined every time I pass it somewhere. The variable is always set in production. What is the right way to type environment variables in a Node.js project?
- process.env
- @types/node
- Configuration
-
How do I import a JSON file in checked JavaScript?
I import catalog.json into an ES module and want its fields checked. Which jsconfig.json settings does that need, and why does tsc ask for an import attribute?
- JSON modules
- resolveJsonModule
- Import attributes
-
How do I add a property to a type from another package?
My authentication middleware sets req.user, and every handler reads it. tsc says Property 'user' does not exist on type Request. How do I add a property to a type that @types/express owns?
- Module augmentation
- declare global
- Express
- .d.ts
Node and platforms
Node.js callbacks, events, streams, child processes, command-line options, timers and buffers; the DOM, fetch, storage, custom elements and sockets.
-
How do I type Node.js fs callbacks and fs/promises in JavaScript?
I read files with node:fs, sometimes with callbacks and sometimes with fs/promises. What do I need for tsc to type the error and the data, and why is my file content a Buffer?
- node:fs
- @types/node
- Callbacks
- Promises
-
How do I type a Node.js EventEmitter's events in JavaScript?
My OrderEvents class extends EventEmitter. Listeners get any for their arguments, and a misspelled event name is never caught. Can JSDoc tell tsc which events exist and what they carry?
- EventEmitter
- @extends
- @types/node
-
How do I type Node.js streams in checked JavaScript?
I read large CSV files with streams. Which parts of a stream pipeline does tsc type for me, and where do I need JSDoc, especially for the chunks that come out of a stream?
- Streams
- readline
- pipeline
- @types/node
-
How do I type child_process results in Node.js JavaScript?
My build script runs git and npm with child_process. What types do execFile and spawn give me in JSDoc-checked code, and why does tsc say stdout might be null?
- child_process
- promisify
- @types/node
-
How do I type a CLI's options with util.parseArgs?
My command-line tool parses --port and --open with util.parseArgs from Node.js. Can tsc know that values.port is a string and values.open a boolean, without a library and without writing the types twice?
- parseArgs
- CLI
- @types/node
-
What type does setTimeout return in JavaScript, Node.js or browser?
I store a timer in a variable typed as a number, and tsc says Timeout is not assignable to number. The same code passes in my browser project. What is the right type for a timer handle?
- setTimeout
- ReturnType
- @types/node
-
How do I type Buffers and Uint8Arrays in JSDoc?
Some of my functions take file contents from fs (a Buffer) and others get bytes from TextEncoder or fetch (a Uint8Array). What type should the @param use, and why does toString('hex') fail on one of them?
- Buffer
- Uint8Array
- @types/node
-
How do I type a Cloudflare Workers fetch handler and its bindings in JavaScript?
My Cloudflare Workers project is plain JavaScript. How do I get request, env and ctx typed in the fetch handler, and how do I tell tsc which bindings env has?
- Cloudflare Workers
- @satisfies
- ExportedHandler
-
How do I type document.querySelector results in JavaScript?
document.querySelector('#email').value fails with 'possibly null' and 'Property value does not exist on type Element'. The element is always on the page. What is the right way to get an input element in JSDoc-checked code?
- DOM
- querySelector
- instanceof
- Type casts
-
How do I type event handlers and addEventListener in JavaScript?
In my input handler, event.target.value fails because target is an EventTarget, and new FormData(event.currentTarget) is refused. How should event handlers be typed in a checked .js file?
- DOM
- addEventListener
- Events
-
How do I type the JSON from a fetch response in JavaScript?
await response.json() gives me any, so a typo in a field name is never caught. How do I type what my API returns in a JSDoc-checked fetch call?
- fetch
- Response.json
- @type
-
How do I type values from localStorage in JavaScript?
I keep the cart in localStorage as JSON. tsc complains that getItem might return null, and everything I read back is any. How do I type stored values in a JSDoc-checked project?
- localStorage
- sessionStorage
- @template
-
How do I type a custom element (Web Component) in JavaScript?
I wrote a price-tag custom element in plain JavaScript. Inside the class everything is typed, but document.querySelector('price-tag') gives me Element and my cents property is unknown. How do I connect the tag name to the class?
- Web Components
- Custom elements
- HTMLElementTagNameMap
-
How do I type WebSocket and EventSource messages in JavaScript?
My page receives stock and price updates over a WebSocket and an EventSource. event.data is any, so a wrong field name is never caught. How do I type the messages in JSDoc?
- WebSocket
- EventSource
- Discriminated unions
- Type guards
Frameworks and tests
Express handlers and middleware, React and Svelte components written in JavaScript, test files, and publishing a package with types.
-
How do I type an Express handler in JavaScript?
My Express handlers are plain JavaScript, so req.params and req.body are untyped. How do I get them checked without moving to TypeScript?
- Express
- @satisfies
- @import
- @types/express
-
How do I type Express middleware and error handlers in JavaScript?
My Express app has middleware that stamps each request, a requireRole() factory and an error handler. How do I type them in JSDoc so res.locals and the error handler's arguments are checked?
- Express
- Middleware
- @satisfies
- @types/express
-
How do I type React component props in a .jsx file?
My React app is written in .jsx files. How do I declare a component's props in JSDoc so a missing or wrong prop is caught where the component is used, without switching to .tsx?
- React
- JSX
- @typedef
- @types/react
-
How do I type Svelte component props in JavaScript?
My Svelte components use plain JavaScript in their script blocks. How do I type a component's props with JSDoc so that svelte-check reports a missing or wrong prop where the component is used?
- Svelte
- svelte-check
- $props
- @type
-
How do I type-check Vitest or Jest test files in JavaScript?
My tests are .js files. When I include them in jsconfig.json, tsc says it cannot find describe and expect. How do I get the test functions typed, so the tests are checked like the code they test?
- Vitest
- Jest
- Test files
- types
-
How do I publish a JavaScript package with types?
I generate .d.ts files for my JavaScript package, but a project that installs it still gets 'Could not find a declaration file'. What does package.json need so that tsc and editors find the types?
- package.json exports
- .d.ts
- npm
-
How do I create .d.ts files from my JavaScript with tsc?
My library is plain JavaScript with JSDoc. People who install it get no types. How do I have tsc write .d.ts files from the JSDoc, without compiling or changing my .js files?
- .d.ts
- declaration
- emitDeclarationOnly