{
  "shapes": [
    {
      "id": "node-esm",
      "slug": "node-esm",
      "title": "Node.js with ES modules",
      "label": "Node.js (ES modules)",
      "summary": "Type-check a Node.js project written as ES modules: the jsconfig.json, @types/node, the npm scripts and the import rules that nodenext enforces.",
      "intro": [
        "This is the setup for a Node.js app, script or server written as ES modules: `\"type\": \"module\"` in package.json, `import` and `export` in your `.js` files. TypeScript checks it with `module` and `moduleResolution` set to `nodenext`, which follow the rules Node.js itself uses to load files.",
        "The jsconfig.json, the package.json scripts and the two files at the end of this page are one real project, checked with TypeScript 5.9 every time this site is built."
      ],
      "packageJson": {
        "type": "module"
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "nodenext",
          "moduleResolution": "nodenext",
          "target": "es2024",
          "types": [
            "node"
          ],
          "skipLibCheck": true
        },
        "include": [
          "src/**/*.js"
        ]
      },
      "devDependencies": [
        "typescript",
        "@types/node"
      ],
      "scripts": {
        "lint": "tsc -p jsconfig.json",
        "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput"
      },
      "editorNotes": [
        "VS Code reads this jsconfig.json with no further setup. To make it use the same TypeScript as `npm run lint`, see [VS Code](/editors/vs-code/)."
      ],
      "example": "start/node-esm",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "TypeScript is a development dependency: it checks your code and never runs in production. `@types/node` gives the checker the types of Node's built-in modules (`node:fs`, `node:http`, `process` and the rest). Pick the `@types/node` major version that matches the Node.js you run, for example `@types/node@24` for Node.js 24."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npm install --save-dev typescript @types/node"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "`nodenext` makes TypeScript resolve imports the way Node.js does: a relative import needs its file extension (`./config.js`, not `./config`), and a package's `exports` field decides what you can import from it. `target: \"es2024\"` matches the JavaScript that current Node.js releases run.",
            "`types: [\"node\"]` loads only Node's types as globals, so an unrelated `@types` package somewhere in `node_modules` cannot add globals you never use. `skipLibCheck` skips checking the `.d.ts` files of your dependencies, which saves time and avoids errors you cannot fix; your own files are still checked in full."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"target\": \"es2024\",\n    \"types\": [\"node\"],\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "scripts",
          "heading": "npm scripts",
          "body": [
            "`lint` checks the whole project once, which is what CI should run. `lint:watch` keeps `tsc` running and checks again on every save; `--preserveWatchOutput` keeps the earlier output on screen instead of clearing it."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"type\": \"module\",\n  \"scripts\": {\n    \"lint\": \"tsc -p jsconfig.json\",\n    \"lint:watch\": \"tsc -p jsconfig.json --watch --preserveWatchOutput\"\n  }\n}"
            }
          ]
        },
        {
          "id": "imports",
          "heading": "What nodenext checks in your imports",
          "body": [
            "With this config, the checker holds your imports to Node's rules:"
          ],
          "list": [
            "relative imports carry their extension: `import { loadConfig } from \"./config.js\"`; leaving it off is TS2835, shown below",
            "built-in modules work with or without the `node:` prefix; the prefix makes it obvious which imports are built in",
            "top-level `await` is allowed, because ES modules allow it (the example's `main.js` uses it)"
          ],
          "examples": [
            "start/node-esm-extension"
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "A two-file server: `src/config.js` reads and types a JSON config file, and `src/main.js` imports it and starts an HTTP server. Both files are checked with the jsconfig.json above."
          ],
          "examples": [
            "start/node-esm",
            "start/node-esm#src/main.js"
          ]
        }
      ],
      "related": [
        "config/check-js",
        "config/module",
        "config/types",
        "editors/vs-code",
        "tools/jsconfig-generator",
        "recipes/type-an-express-handler"
      ],
      "sources": [
        {
          "title": "Modules: Packages, \"type\"",
          "url": "https://nodejs.org/api/packages.html#type",
          "publisher": "Node.js docs"
        },
        {
          "title": "Modules: ECMAScript modules",
          "url": "https://nodejs.org/api/esm.html",
          "publisher": "Node.js docs"
        },
        {
          "title": "Modules: Reference",
          "url": "https://www.typescriptlang.org/docs/handbook/modules/reference.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "@types/node",
          "url": "https://www.npmjs.com/package/@types/node",
          "publisher": "npm"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "node-cjs",
      "slug": "node-cjs",
      "title": "Node.js with CommonJS",
      "label": "Node.js (CommonJS)",
      "summary": "Type-check a CommonJS Node.js project: require and module.exports with JSDoc, @types/node, and module nodenext reading \"type\": \"commonjs\".",
      "intro": [
        "This is the setup for a Node.js project written as CommonJS: `\"type\": \"commonjs\"` in package.json (or no `\"type\"` at all), `require` and `module.exports` in your `.js` files. TypeScript understands that style in JavaScript files: a `require` call is an import, and `module.exports` and `exports.name` are exports, typed from your JSDoc.",
        "The config is the same as for ES modules. `module: \"nodenext\"` reads `\"type\"` from package.json and checks each file the way Node.js will load it. The files below are one project, checked with TypeScript 5.9 when this site is built."
      ],
      "packageJson": {
        "type": "commonjs"
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "nodenext",
          "moduleResolution": "nodenext",
          "target": "es2024",
          "types": [
            "node"
          ],
          "skipLibCheck": true
        },
        "include": [
          "src/**/*.js"
        ]
      },
      "devDependencies": [
        "typescript",
        "@types/node"
      ],
      "scripts": {
        "lint": "tsc -p jsconfig.json",
        "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput"
      },
      "editorNotes": [
        "VS Code reads this jsconfig.json with no further setup. To make it use the same TypeScript as `npm run lint`, see [VS Code](/editors/vs-code/)."
      ],
      "example": "start/node-cjs",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "TypeScript is a development dependency: it checks your code and never runs in production. `@types/node` gives the checker the types of Node's built-in modules (`node:fs`, `node:http`, `process` and the rest). Pick the `@types/node` major version that matches the Node.js you run, for example `@types/node@24` for Node.js 24."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npm install --save-dev typescript @types/node"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "Identical to the [ES modules setup](/start/node-esm/): `nodenext` takes the module format from package.json, so the same config checks either kind of project, and a project that mixes `.cjs` and `.mjs` files."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"target\": \"es2024\",\n    \"types\": [\"node\"],\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "scripts",
          "heading": "npm scripts",
          "body": [
            "`\"type\": \"commonjs\"` is also what Node.js assumes when there is no `\"type\"`; writing it makes the choice explicit, as Node.js's documentation recommends. `lint` checks once; `lint:watch` checks again on every save."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"type\": \"commonjs\",\n  \"scripts\": {\n    \"lint\": \"tsc -p jsconfig.json\",\n    \"lint:watch\": \"tsc -p jsconfig.json --watch --preserveWatchOutput\"\n  }\n}"
            }
          ]
        },
        {
          "id": "require",
          "heading": "What the checker reads from require and module.exports",
          "body": [
            "In a CommonJS `.js` file:"
          ],
          "list": [
            "`const { formatPrice } = require(\"./format\")` is an import; a name the module does not export is an error, with a suggestion (TS2724), as below",
            "relative `require` calls may leave out `.js`, because Node.js tries the extension for CommonJS",
            "`module.exports = { … }` and `exports.name = …` are the module's exports, typed from the JSDoc on what you assign",
            "since TypeScript 5.8, `require()` of an ES module is allowed under `nodenext`, as in Node.js from 20.19 and 22.12; Node.js still refuses an ES module that uses top-level `await`"
          ],
          "examples": [
            "start/node-cjs-typo"
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "`src/format.js` exports a typed helper with `module.exports`; `src/main.js` requires it and Node's `http` module and starts a server."
          ],
          "examples": [
            "start/node-cjs",
            "start/node-cjs#src/format.js"
          ]
        }
      ],
      "related": [
        "start/node-esm",
        "config/module",
        "config/verbatim-module-syntax",
        "config/check-js"
      ],
      "sources": [
        {
          "title": "Modules: Packages, \"type\"",
          "url": "https://nodejs.org/api/packages.html#type",
          "publisher": "Node.js docs"
        },
        {
          "title": "Modules: CommonJS modules",
          "url": "https://nodejs.org/api/modules.html",
          "publisher": "Node.js docs"
        },
        {
          "title": "Type Checking JavaScript Files",
          "url": "https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Modules: Reference",
          "url": "https://www.typescriptlang.org/docs/handbook/modules/reference.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "TypeScript 5.8 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "@types/node",
          "url": "https://www.npmjs.com/package/@types/node",
          "publisher": "npm"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "browser",
      "slug": "browser",
      "title": "Browser, no bundler",
      "headline": "Type-checked JavaScript in the browser, without a bundler",
      "label": "Browser (no bundler)",
      "hideToggles": [
        "nodeTypes"
      ],
      "summary": "Type-check JavaScript that the browser loads as module scripts: the DOM in lib, no Node.js types, and imports held to the exact paths a browser needs.",
      "intro": [
        "This is the setup for plain web pages: `.js` files loaded with `<script type=\"module\">`, no bundler, no build step. The checker needs to know two things about that environment: the browser's APIs are there (`lib` with `dom`), and Node's are not (`types: []`).",
        "TypeScript has no option made for unbundled browser code. Its handbook suggests `module: \"nodenext\"` with `\"type\": \"module\"` in package.json, because that holds relative imports to full file names, which is what a browser needs to load them. The files below are one project, checked with TypeScript 5.9 when this site is built."
      ],
      "packageJson": {
        "type": "module"
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "nodenext",
          "moduleResolution": "nodenext",
          "target": "es2022",
          "lib": [
            "es2022",
            "dom",
            "dom.iterable"
          ],
          "types": []
        },
        "include": [
          "src/**/*.js"
        ]
      },
      "devDependencies": [
        "typescript"
      ],
      "scripts": {
        "lint": "tsc -p jsconfig.json",
        "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput"
      },
      "editorNotes": [
        "VS Code reads this jsconfig.json with no further setup. To make it use the same TypeScript as `npm run lint`, see [VS Code](/editors/vs-code/)."
      ],
      "example": "start/browser",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "TypeScript is the only package: it checks your files on your machine and in CI, and nothing from it is sent to the browser. A `package.json` is still useful, for the scripts and for `\"type\": \"module\"`."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npm init -y\nnpm pkg set type=module\nnpm install --save-dev typescript"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "`lib` lists the ECMAScript year your browsers support plus `dom` (and `dom.iterable`, so a `NodeList` works in `for…of`). `types: []` keeps any `@types` package in `node_modules` from adding Node.js globals such as `process`, which would make code that crashes in a browser look fine.",
            "`module` and `moduleResolution` set to `nodenext`, with `\"type\": \"module\"`, make the checker refuse `import \"./counter\"` without its extension (TS2835), the same import a browser could not load."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"target\": \"es2022\",\n    \"lib\": [\"es2022\", \"dom\", \"dom.iterable\"],\n    \"types\": []\n  },\n  \"include\": [\"src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "scripts",
          "heading": "npm scripts",
          "body": [
            "`lint` checks every file once; run it in CI. `lint:watch` checks again on every save."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"type\": \"module\",\n  \"scripts\": {\n    \"lint\": \"tsc -p jsconfig.json\",\n    \"lint:watch\": \"tsc -p jsconfig.json --watch --preserveWatchOutput\"\n  }\n}"
            }
          ]
        },
        {
          "id": "html",
          "heading": "The page",
          "body": [
            "One `<script type=\"module\">` loads the entry file; everything else is reached through `import`, and the checker follows the same imports."
          ],
          "snippets": [
            {
              "file": "index.html",
              "from": "start/browser",
              "lang": "html",
              "code": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Counter</title>\n    <script type=\"module\" src=\"./src/main.js\"></script>\n  </head>\n  <body>\n    <button id=\"counter\" type=\"button\">Click me</button>\n    <ul><li>One</li><li>Two</li></ul>\n  </body>\n</html>"
            }
          ]
        },
        {
          "id": "dom",
          "heading": "What the checker knows about the page",
          "body": [
            "It knows the DOM's types, not your HTML. `document.querySelector(\"button\")` is an `HTMLButtonElement | null`: the tag name picks the element type, and `null` stays because the page may not have one, as the first example below shows. `querySelector(\"#counter\")` is just an `Element | null`. An `instanceof HTMLButtonElement` check, as in the example project, narrows it to the type you need."
          ],
          "examples": [
            "start/browser-null",
            "start/browser-extension"
          ]
        },
        {
          "id": "packages",
          "heading": "npm packages without a bundler",
          "body": [
            "A browser cannot resolve `import \"lodash\"` by itself; an import map in the page can map the name to a URL. TypeScript does not read import maps. Its handbook suggests `paths` to point the checker at type declarations for such names, and installing the package's types with npm so there is something to point at."
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "`src/main.js` finds a button and passes it to `attachCounter` from `src/counter.js`, after narrowing it with `instanceof`."
          ],
          "examples": [
            "start/browser",
            "start/browser#src/counter.js"
          ]
        }
      ],
      "related": [
        "config/lib",
        "config/types",
        "config/module-resolution",
        "types/null-and-undefined"
      ],
      "sources": [
        {
          "title": "Modules: Choosing Compiler Options",
          "url": "https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "TSConfig Reference: lib",
          "url": "https://www.typescriptlang.org/tsconfig/#lib",
          "publisher": "TypeScript"
        },
        {
          "title": "JavaScript modules",
          "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules",
          "publisher": "MDN"
        },
        {
          "title": "Document: querySelector() method",
          "url": "https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector",
          "publisher": "MDN"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "library",
      "slug": "library",
      "title": "Library (publishing types)",
      "headline": "Type-checked JavaScript for a library that ships its types",
      "label": "Library (ships .d.ts)",
      "summary": "Publish a JavaScript package with types: JSDoc in the source, a second config that writes .d.ts files, and an exports map with a types condition.",
      "intro": [
        "A package written in JavaScript can give its users full types without any TypeScript source. You write JSDoc as usual; a second config tells `tsc` to write `.d.ts` files from it; `package.json` points TypeScript at them. Users get completions and checking in their editor, whether their own code is TypeScript or checked JavaScript.",
        "The source, the config that writes the declarations, and the `.d.ts` file shown below are one project. When this site is built, the source is checked and the declarations are written again in memory with TypeScript 5.9; the build fails if they no longer match the file on this page."
      ],
      "packageJson": {
        "type": "module",
        "exports": {
          ".": {
            "types": "./types/index.d.ts",
            "default": "./src/index.js"
          }
        },
        "files": [
          "src",
          "types"
        ]
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "node18",
          "target": "es2022",
          "types": [],
          "skipLibCheck": true
        },
        "include": [
          "src/**/*.js"
        ]
      },
      "devDependencies": [
        "typescript"
      ],
      "scripts": {
        "lint": "tsc -p jsconfig.json",
        "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput",
        "types": "tsc -p jsconfig.types.json",
        "prepack": "npm run types"
      },
      "editorNotes": [
        "VS Code reads this jsconfig.json with no further setup. To make it use the same TypeScript as `npm run lint`, see [VS Code](/editors/vs-code/).",
        "Editors use `jsconfig.json`, so while you work they check your source and never write files. Only `npm run types` writes the `.d.ts` files."
      ],
      "example": "start/library",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "A library's own tooling needs only TypeScript. Add `@types/node` (and `\"node\"` in `types`) if the library uses Node.js APIs."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npm install --save-dev typescript"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "This config checks; it never writes. TypeScript's handbook suggests `module: \"node18\"` for libraries: code that works in Node.js almost always works in bundlers too, and the setting stays put across TypeScript releases. Set `target` to the oldest JavaScript version you support, since it decides which built-in methods the checker lets you use."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"node18\",\n    \"target\": \"es2022\",\n    \"types\": [],\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "types-config",
          "heading": "The config that writes the .d.ts files",
          "body": [
            "It extends `jsconfig.json` and switches writing on for declarations only. `\"noEmit\": false` is required: a config that extends a `jsconfig.json` inherits its `noEmit: true`, and `tsc` would then write nothing, without an error. See [`declaration`](/config/declaration/) for each option."
          ],
          "snippets": [
            {
              "file": "jsconfig.types.json",
              "from": "start/library",
              "lang": "json",
              "code": "{\n  \"extends\": \"./jsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"declaration\": true,\n    \"emitDeclarationOnly\": true,\n    \"declarationDir\": \"types\"\n  }\n}"
            }
          ]
        },
        {
          "id": "package-json",
          "heading": "package.json",
          "body": [
            "`exports` lists the `types` condition first, then `default`; TypeScript's 4.7 release notes say the `types` condition should come first. `files` publishes the source and the declarations. `prepack` runs `npm run types` before `npm pack` and `npm publish`, so the `.d.ts` files are always fresh and can stay out of git."
          ],
          "snippets": [
            {
              "file": "package.json",
              "from": "start/library",
              "lang": "json",
              "code": "{\n  \"name\": \"tiny-slug\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./types/index.d.ts\",\n      \"default\": \"./src/index.js\"\n    }\n  },\n  \"files\": [\"src\", \"types\"],\n  \"scripts\": {\n    \"lint\": \"tsc -p jsconfig.json\",\n    \"lint:watch\": \"tsc -p jsconfig.json --watch --preserveWatchOutput\",\n    \"types\": \"tsc -p jsconfig.types.json\",\n    \"prepack\": \"npm run types\"\n  }\n}"
            }
          ]
        },
        {
          "id": "what-users-get",
          "heading": "What users of the package get",
          "body": [
            "This is the file `npm run types` writes from `src/index.js`. The `@typedef` became an exported type, the optional parameter stayed optional, and the JSDoc descriptions came along for hovers."
          ],
          "examples": [
            "start/library#types/index.d.ts"
          ]
        },
        {
          "id": "commonjs-users",
          "heading": "Users who still write CommonJS",
          "body": [
            "The package is ES modules only. Node.js lets CommonJS code `require()` it without a flag from 20.19 and 22.12, as long as it has no top-level `await`, and TypeScript 5.8 and later model that under `module: \"nodenext\"`. Supporting older Node.js from CommonJS would need a second, CommonJS build, which is beyond a no-build setup."
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "One module with an options object: the kind of API where JSDoc types help users most."
          ],
          "examples": [
            "start/library"
          ]
        }
      ],
      "related": [
        "config/declaration",
        "config/no-emit",
        "tags/typedef",
        "config/skip-lib-check"
      ],
      "sources": [
        {
          "title": "Creating .d.ts Files from .js files",
          "url": "https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Modules: Choosing Compiler Options",
          "url": "https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "TypeScript 4.7 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "TSConfig Reference: declaration",
          "url": "https://www.typescriptlang.org/tsconfig/#declaration",
          "publisher": "TypeScript"
        },
        {
          "title": "Modules: Packages, conditional exports",
          "url": "https://nodejs.org/api/packages.html#conditional-exports",
          "publisher": "Node.js docs"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "cloudflare-workers",
      "slug": "cloudflare-workers",
      "title": "Cloudflare Workers",
      "label": "Cloudflare Workers",
      "refLabel": "Cloudflare",
      "ownPageOnly": [
        "types"
      ],
      "hideToggles": [
        "nodeTypes"
      ],
      "summary": "Type-check a Cloudflare Workers project written in JavaScript: the runtime types, an Env typedef for your bindings, @satisfies on the handler, no DOM in lib.",
      "intro": [
        "This is the setup for a Cloudflare Workers project written in JavaScript. The runtime is not a browser and not Node.js, so the checker gets its own types: `@cloudflare/workers-types` declares `Request`, `Response`, `ExecutionContext` and the rest of the runtime, and `lib` leaves the DOM out.",
        "Your handler is typed with one JSDoc tag, `@satisfies {ExportedHandler<Env>}`, which types `request`, `env` and `ctx` for you, and `Env` is a `@typedef` that lists your bindings. The files below are one project, checked with TypeScript 5.9 when this site is built."
      ],
      "packageJson": {
        "type": "module"
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "esnext",
          "moduleResolution": "bundler",
          "target": "es2024",
          "lib": [
            "es2024"
          ],
          "types": [
            "@cloudflare/workers-types"
          ],
          "skipLibCheck": true
        },
        "include": [
          "src/**/*.js"
        ]
      },
      "devDependencies": [
        "typescript",
        "@cloudflare/workers-types"
      ],
      "scripts": {
        "lint": "tsc -p jsconfig.json",
        "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput"
      },
      "editorNotes": [
        "VS Code reads this jsconfig.json with no further setup. To make it use the same TypeScript as `npm run lint`, see [VS Code](/editors/vs-code/)."
      ],
      "example": "start/cloudflare-workers",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "Both packages are development dependencies. Cloudflare publishes `@cloudflare/workers-types` with the types of the latest runtime; the [`wrangler types`](#wrangler-types) section below covers the generator Cloudflare recommends for a project's own types."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npm install --save-dev typescript @cloudflare/workers-types"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "`lib: [\"es2024\"]` gives you the ECMAScript library and no DOM, and `types` loads the runtime's own declarations, which include `fetch`, `Request`, `Response` and `console`. `moduleResolution: \"bundler\"` matches how Wrangler bundles your code before upload, so extensionless relative imports are accepted."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"target\": \"es2024\",\n    \"lib\": [\"es2024\"],\n    \"types\": [\"@cloudflare/workers-types\"],\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "scripts",
          "heading": "npm scripts",
          "body": [
            "`lint` checks the project; it does not need Wrangler or an account. Run it before `wrangler deploy`, or in CI."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"type\": \"module\",\n  \"scripts\": {\n    \"lint\": \"tsc -p jsconfig.json\",\n    \"lint:watch\": \"tsc -p jsconfig.json --watch --preserveWatchOutput\"\n  }\n}"
            }
          ]
        },
        {
          "id": "env",
          "heading": "Typing Env",
          "body": [
            "Bindings (variables, secrets, storage, other services) arrive on the `env` parameter. Describe them in a `@typedef` with the same names as in your Wrangler config; a misspelled name is then an error, as below. Keep the two in step by hand, or let `wrangler types` write the type for you."
          ],
          "snippets": [
            {
              "file": "wrangler.jsonc",
              "from": "start/cloudflare-workers",
              "lang": "jsonc",
              "code": "{\n  \"name\": \"hello\",\n  \"main\": \"src/index.js\",\n  \"compatibility_date\": \"2026-10-01\",\n  \"vars\": {\n    \"GREETING\": \"Hello\"\n  }\n}"
            }
          ],
          "examples": [
            "start/cloudflare-workers-env"
          ]
        },
        {
          "id": "wrangler-types",
          "heading": "wrangler types",
          "body": [
            "Cloudflare's documentation recommends `npx wrangler types` for a project's runtime types: it writes `worker-configuration.d.ts`, with an `Env` type from your bindings and runtime types matched to your compatibility date and flags. Point `types` at that file instead of the package, delete your own `Env` typedef (the global one replaces it), and run the command again after every config change; `wrangler types --check` fails CI when the file is out of date.",
            "On 20 October 2026 we checked the handler on this page, without its `Env` typedef, against the file Wrangler 4.151 wrote for this config: no errors. The examples keep the package, because the written file is large and tied to one Wrangler release. If you turn on Node.js compatibility, the same documentation says to install `@types/node` and add `\"node\"` to `types`."
          ],
          "snippets": [
            {
              "file": "jsconfig.json, with wrangler types",
              "lang": "jsonc",
              "code": "{\n  \"compilerOptions\": {\n    // …the options above, with types pointing at the written file\n    \"types\": [\"./worker-configuration.d.ts\"]\n  }\n}"
            }
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "A fetch handler that reads a variable binding and schedules work after the response with `ctx.waitUntil`."
          ],
          "examples": [
            "start/cloudflare-workers"
          ]
        }
      ],
      "related": [
        "tags/satisfies",
        "tags/typedef",
        "config/lib",
        "config/types"
      ],
      "sources": [
        {
          "title": "Languages: JavaScript",
          "url": "https://developers.cloudflare.com/workers/languages/javascript/",
          "publisher": "Cloudflare docs"
        },
        {
          "title": "Languages: TypeScript",
          "url": "https://developers.cloudflare.com/workers/languages/typescript/",
          "publisher": "Cloudflare docs"
        },
        {
          "title": "@cloudflare/workers-types",
          "url": "https://www.npmjs.com/package/@cloudflare/workers-types",
          "publisher": "npm"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "deno",
      "slug": "deno",
      "title": "Deno",
      "label": "Deno",
      "hideToggles": [
        "nodeTypes",
        "skipLibCheck",
        "resolveJsonModule",
        "watchScript"
      ],
      "summary": "Type-check JavaScript with Deno: deno check, checkJs and JSDoc, a jsconfig.json Deno reads next to package.json, and when Deno checks types.",
      "intro": [
        "Deno ships with TypeScript's checker built in, so there is nothing to install: `deno check` checks your project. Per the Deno documentation, it checks `.js` files when you ask for it: `checkJs` in the config, a `// @ts-check` comment, or `deno check --check-js`. JSDoc types work the same way as with `tsc`.",
        "Also per the Deno documentation, since Deno 2.1 a `jsconfig.json` is picked up when the project has a `package.json`, which is the setup below. We did not run Deno for this page: the example is checked with `tsc` (TypeScript 5.9) using the same `jsconfig.json`, and it uses only web APIs that both checkers know."
      ],
      "packageJson": {
        "type": "module"
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "nodenext",
          "types": []
        }
      },
      "devDependencies": [],
      "scripts": {
        "lint": "deno check ."
      },
      "editorNotes": [
        "Per the Deno documentation, VS Code needs the Deno extension, turned on for the workspace with the command \"Deno: Initialize Workspace Configuration\"; other editors start Deno's language server with `deno lsp`. Without it, the editor's built-in TypeScript checks the file as `tsc` would and does not know the `Deno` namespace."
      ],
      "example": "start/deno",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "Nothing beyond Deno. `deno task lint` runs the script below: per the Deno documentation, `deno task` falls back to the `scripts` in `package.json` when there is no task of that name in a `deno.json`."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "deno task lint"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "`checkJs` is the line that matters to Deno; its default is `false`, so without it `deno check` skips `.js` files. Deno is strict by default, and `strict` keeps `tsc` in step. `module: \"nodenext\"` is Deno's own default and lets `tsc` read the file as an ES module. The config sets no `lib`, so each checker uses its own default: Deno its runtime types, `tsc` the standard library with the DOM.",
            "Per the Deno documentation, Deno ignores options that only matter for writing output and warns about options it does not use in a `tsconfig.json`."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"nodenext\",\n    \"types\": []\n  }\n}"
            }
          ]
        },
        {
          "id": "scripts",
          "heading": "npm scripts",
          "body": [
            "`deno check .` checks every JavaScript and TypeScript file under the folder."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"type\": \"module\",\n  \"scripts\": {\n    \"lint\": \"deno check .\"\n  }\n}"
            }
          ]
        },
        {
          "id": "when-deno-checks",
          "heading": "When Deno checks, and when it does not",
          "body": [
            "Per the Deno documentation:"
          ],
          "list": [
            "`deno run` does not check types; `deno run --check` does, before running",
            "`deno test` and `deno bench` check by default",
            "`deno check` checks local files; `--all` also checks remote modules and npm packages"
          ]
        },
        {
          "id": "deno-json",
          "heading": "Or a deno.json",
          "body": [
            "A Deno-only project can skip `package.json` and `jsconfig.json` and turn on checking in `deno.json`. Per the Deno documentation, `compilerOptions` in `deno.json` take precedence over a `tsconfig.json` or `jsconfig.json` when both exist."
          ],
          "snippets": [
            {
              "file": "deno.json",
              "lang": "jsonc",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true\n  },\n  \"tasks\": {\n    \"lint\": \"deno check .\"\n  }\n}"
            }
          ]
        },
        {
          "id": "deno-globals",
          "heading": "Deno's own APIs",
          "body": [
            "Code that uses the `Deno` namespace (`Deno.serve`, `Deno.readTextFile`) is checked by `deno check`, which has Deno's types; `tsc` does not know them. For `deno serve`, the Deno documentation types the default export with `Deno.ServeDefaultExport`, which in a `.js` file is a [`@satisfies`](/tags/satisfies/) tag on the exported object. The example below stays with `Request` and `Response`, which both checkers understand, so this site can check it."
          ],
          "examples": [
            "start/deno-typo"
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "A default export with a `fetch` method, the shape `deno serve` expects, answering every request with JSON."
          ],
          "examples": [
            "start/deno"
          ]
        }
      ],
      "related": [
        "config/check-js",
        "config/ts-check",
        "config/module",
        "tags/param"
      ],
      "sources": [
        {
          "title": "deno check",
          "url": "https://docs.deno.com/runtime/reference/cli/check/",
          "publisher": "Deno docs"
        },
        {
          "title": "TypeScript support",
          "url": "https://docs.deno.com/runtime/fundamentals/typescript/",
          "publisher": "Deno docs"
        },
        {
          "title": "Configuring TypeScript",
          "url": "https://docs.deno.com/runtime/reference/ts_config_migration/",
          "publisher": "Deno docs"
        },
        {
          "title": "deno serve",
          "url": "https://docs.deno.com/runtime/reference/cli/serve/",
          "publisher": "Deno docs"
        },
        {
          "title": "deno task",
          "url": "https://docs.deno.com/runtime/reference/cli/task/",
          "publisher": "Deno docs"
        },
        {
          "title": "Set up your environment",
          "url": "https://docs.deno.com/runtime/getting_started/setup_your_environment/",
          "publisher": "Deno docs"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "bun",
      "slug": "bun",
      "title": "Bun",
      "label": "Bun",
      "hideToggles": [
        "nodeTypes"
      ],
      "summary": "Type-check JavaScript that Bun runs: @types/bun, the module settings Bun recommends, and tsc -p jsconfig.json, since running a file does not check it.",
      "intro": [
        "Bun runs JavaScript and TypeScript files directly, and per the Bun documentation it runs them without checking their types. Checking is a separate step: `tsc -p jsconfig.json` with Bun's types installed, or Bun's own `bun check` (below).",
        "The config follows the compiler options Bun's documentation recommends, adapted for `.js` files: `checkJs` instead of `allowJs`, and none of the options that only matter for `.ts` files. The files below are one project, checked with TypeScript 5.9 when this site is built."
      ],
      "packageJson": {
        "type": "module"
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "preserve",
          "moduleResolution": "bundler",
          "target": "esnext",
          "lib": [
            "esnext"
          ],
          "moduleDetection": "force",
          "types": [
            "bun"
          ],
          "skipLibCheck": true
        },
        "include": [
          "src/**/*.js"
        ]
      },
      "devDependencies": [
        "typescript",
        "@types/bun"
      ],
      "scripts": {
        "lint": "tsc -p jsconfig.json",
        "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput"
      },
      "editorNotes": [
        "VS Code reads this jsconfig.json with no further setup. To make it use the same TypeScript as `npm run lint`, see [VS Code](/editors/vs-code/)."
      ],
      "example": "start/bun",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "`@types/bun` declares the `Bun` global and Bun's modules; per the Bun documentation, install it with `bun add -d @types/bun`. TypeScript provides `tsc`. Install both as development dependencies (with npm, `npm install --save-dev typescript @types/bun`):"
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "bun add -d typescript @types/bun"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "`module: \"preserve\"` with `moduleResolution: \"bundler\"` matches how Bun resolves imports, `lib: [\"esnext\"]` with `types: [\"bun\"]` gives Bun's APIs and no DOM, and `moduleDetection: \"force\"` treats every file as a module, as Bun does. Bun's recommended config also turns on `noUncheckedIndexedAccess`, `noImplicitOverride` and `noFallthroughCasesInSwitch`; add them if you want the stricter level."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"preserve\",\n    \"moduleResolution\": \"bundler\",\n    \"target\": \"esnext\",\n    \"lib\": [\"esnext\"],\n    \"moduleDetection\": \"force\",\n    \"types\": [\"bun\"],\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "scripts",
          "heading": "npm scripts",
          "body": [
            "Run them with `bun run lint` or `npm run lint`; either way it is `tsc` that checks."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"type\": \"module\",\n  \"scripts\": {\n    \"lint\": \"tsc -p jsconfig.json\",\n    \"lint:watch\": \"tsc -p jsconfig.json --watch --preserveWatchOutput\"\n  }\n}"
            }
          ]
        },
        {
          "id": "bun-check",
          "heading": "bun check",
          "body": [
            "The Bun documentation (Bun 1.4) describes a built-in checker, `bun check`, that reads `tsconfig.json`, writes no files, and aims to report the same errors as `tsc` from TypeScript 7. It reports errors in JavaScript files when `checkJs` is on. That documentation talks about `tsconfig.json` and does not mention `jsconfig.json`, so with `bun check` name the file `tsconfig.json`. We have not run it; the example on this page is checked with `tsc` 5.9.",
            "Per the same documentation, `bun --check file.js` checks before it runs, and `bun test --check` checks before testing."
          ]
        },
        {
          "id": "typo",
          "heading": "What it catches",
          "body": [
            "Bun's APIs are typed like any other: a misspelled method on `Bun.file()` is an error."
          ],
          "examples": [
            "start/bun-typo"
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "A small `Bun.serve` server that reads a port from `Bun.env` and a file with `Bun.file`."
          ],
          "examples": [
            "start/bun"
          ]
        }
      ],
      "related": [
        "config/module",
        "config/module-detection",
        "config/types",
        "config/lib"
      ],
      "sources": [
        {
          "title": "TypeScript",
          "url": "https://bun.com/docs/runtime/typescript",
          "publisher": "Bun docs"
        },
        {
          "title": "Type checking",
          "url": "https://bun.com/docs/runtime/check",
          "publisher": "Bun docs"
        },
        {
          "title": "HTTP server",
          "url": "https://bun.com/docs/runtime/http/server",
          "publisher": "Bun docs"
        },
        {
          "title": "@types/bun",
          "url": "https://www.npmjs.com/package/@types/bun",
          "publisher": "npm"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "monorepo",
      "slug": "monorepo",
      "title": "Monorepo",
      "headline": "Type-checked JavaScript in a monorepo",
      "label": "Monorepo (workspaces)",
      "testInclude": "packages/*/test/**/*.js",
      "summary": "Type-check a JavaScript monorepo: one root jsconfig.json for all packages, a small one per package that extends it, and paths to the sources.",
      "intro": [
        "In a repository with several packages, the simplest checked setup is one program for everything: a root `jsconfig.json` includes every package's source, so `npm run lint` at the root checks all of them and every import between them. Each package also gets a small `jsconfig.json` that extends the root, so an editor checks a file with its own package's settings.",
        "Imports between packages use package names (`@acme/util`). `paths` maps those names to the packages' sources, so the checker resolves them with or without `npm install`. The files below are one project, checked with TypeScript 5.9 when this site is built."
      ],
      "packageJson": {
        "private": true,
        "type": "module",
        "workspaces": [
          "packages/*"
        ]
      },
      "jsconfig": {
        "compilerOptions": {
          "checkJs": true,
          "noEmit": true,
          "strict": true,
          "module": "nodenext",
          "moduleResolution": "nodenext",
          "target": "es2024",
          "types": [
            "node"
          ],
          "skipLibCheck": true,
          "paths": {
            "@acme/*": [
              "./packages/*/src/index.js"
            ]
          }
        },
        "include": [
          "packages/*/src/**/*.js"
        ]
      },
      "devDependencies": [
        "typescript",
        "@types/node"
      ],
      "scripts": {
        "lint": "tsc -p jsconfig.json",
        "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput"
      },
      "editorNotes": [
        "VS Code checks each file with the nearest `jsconfig.json` above it, which is its package's. Open the repository root as the workspace so every package's config is found."
      ],
      "example": "start/monorepo",
      "examples": [],
      "sections": [
        {
          "id": "install",
          "heading": "Install",
          "body": [
            "Development dependencies live in the root `package.json`, once for all packages. npm workspaces (`\"workspaces\": [\"packages/*\"]`) link each package into `node_modules` under its name when you run `npm install`."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npm install --save-dev typescript @types/node"
            }
          ]
        },
        {
          "id": "jsconfig",
          "heading": "jsconfig.json",
          "body": [
            "`include` takes every package's `src` folder. `paths` maps `@acme/<name>` to `packages/<name>/src/index.js`, relative to this file. With the workspaces installed, TypeScript would also find `@acme/util` through its link in `node_modules`, and in our test it then still checked the linked package's files; `paths` makes the result independent of whether `npm install` has run."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "json",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"noEmit\": true,\n    \"strict\": true,\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"target\": \"es2024\",\n    \"types\": [\"node\"],\n    \"skipLibCheck\": true,\n    \"paths\": {\n      \"@acme/*\": [\"./packages/*/src/index.js\"]\n    }\n  },\n  \"include\": [\"packages/*/src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "scripts",
          "heading": "npm scripts",
          "body": [
            "One `lint` at the root checks the whole repository in one program, which is faster than checking each package separately and sees every import between them."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"private\": true,\n  \"type\": \"module\",\n  \"workspaces\": [\"packages/*\"],\n  \"scripts\": {\n    \"lint\": \"tsc -p jsconfig.json\",\n    \"lint:watch\": \"tsc -p jsconfig.json --watch --preserveWatchOutput\"\n  }\n}"
            }
          ]
        },
        {
          "id": "per-package",
          "heading": "One small config per package",
          "body": [
            "Each package's `jsconfig.json` extends the root and narrows `include` to its own source. Relative paths set in the root config, such as those in `paths`, stay relative to the root file. This site also checks both package configs on every build."
          ],
          "snippets": [
            {
              "file": "packages/app/jsconfig.json",
              "from": "start/monorepo",
              "lang": "json",
              "code": "{\n  \"extends\": \"../../jsconfig.json\",\n  \"include\": [\"src/**/*.js\"]\n}"
            }
          ]
        },
        {
          "id": "across-packages",
          "heading": "Checking across packages",
          "body": [
            "A call into another package is checked against that package's JSDoc, exactly like an import within one package:"
          ],
          "examples": [
            "start/monorepo-cross-package"
          ]
        },
        {
          "id": "project-references",
          "heading": "Project references and composite",
          "body": [
            "TypeScript's project references split a repository into projects that `tsc -b` checks in order. They are built for projects that write output: a referenced project needs `composite`, which requires declaration files, and `tsc -b` refuses to reference a project with `noEmit` (TS6310, \"Referenced project may not disable emit\", in our test). For JavaScript that would mean writing `.d.ts` files for every package on every check. Use them only if the packages publish types anyway, as in the [library setup](/start/library/); otherwise one root program is simpler."
          ]
        },
        {
          "id": "paths-and-exports",
          "heading": "paths and each package's exports",
          "body": [
            "`paths` sends the checker straight to `src/index.js`, past the package's `exports` map. Keep `exports` pointing at the same file, so what the checker reads is what Node.js loads through the workspace link."
          ]
        },
        {
          "id": "example-project",
          "heading": "The example project",
          "body": [
            "Two packages: `@acme/util` exports a typed helper, `@acme/app` imports it by name."
          ],
          "examples": [
            "start/monorepo",
            "start/monorepo#packages/util/src/index.js"
          ]
        }
      ],
      "related": [
        "config/extends",
        "config/paths",
        "config/include",
        "start/library"
      ],
      "sources": [
        {
          "title": "TSConfig Reference: extends",
          "url": "https://www.typescriptlang.org/tsconfig/#extends",
          "publisher": "TypeScript"
        },
        {
          "title": "TSConfig Reference: paths",
          "url": "https://www.typescriptlang.org/tsconfig/#paths",
          "publisher": "TypeScript"
        },
        {
          "title": "Project References",
          "url": "https://www.typescriptlang.org/docs/handbook/project-references.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "workspaces",
          "url": "https://docs.npmjs.com/cli/using-npm/workspaces",
          "publisher": "npm docs"
        }
      ],
      "updated": "2026-10-20"
    }
  ]
}
