A single deep-dive on Content Mappers, which landed in TypeScript 7.1 Beta (2026-10-20). This isn't a release recap: it reads the design itself, meaning how TypeScript now shows non-TypeScript files like .vue, .svelte and .mdx to its type checker.

Why now

The problem

TypeScript can only type-check TS/JS syntax. Real components, though, are single-file components (SFCs) that mix template, script and style in one file. Three problems have to be solved.

  1. Presenting: turn a non-TS file into a virtual TS file the checker understands.
  2. Mapping back: take errors, hovers and go-to-definition results found in the virtual file and put them at the right place in the original file.
  3. Integrating: give the same result in the CLI (tsc), the editor (LSP), and --build, --watch and incremental builds.

Until now the answer was Volar. Volar converted each .vue file into virtual TS with a map back to the original, and either embedded TypeScript's language service or hooked into TypeScript in roundabout ways. The result was a split world where the editor's language server and the CLI type-check (vue-tsc) were separate systems, and because it leaned on TypeScript internals it broke with TS 7. The easiest way to understand Content Mappers is as the Volar model brought back as a first-class part of TypeScript itself.

How it works

1. Registration: tsconfig and package.json

Users add contentMappers to tsconfig.json as a sibling of compilerOptions, not inside it.

{
  "compilerOptions": { "strict": true, "noEmit": true },
  "contentMappers": [
    {
      "package": "@vue/content-mapper",
      "extensions": [".vue"],
      "options": { "target": 3.5 }
    }
  ],
  "include": ["src/**/*.ts", "src/**/*.vue"]
}

package is resolved as a Node.js module name. The mapper's own package.json declares how to launch it and which compiler options affect its output.

{
  "name": "vue-content-mapper",
  "version": "1.0.0",
  "typescript": {
    "contentMapper": {
      "exec": ["node", "dist/server.js"],
      "compilerOptions": ["module", "jsx", "jsxImportSource"],
      "dynamicConfig": true
    }
  }
}

The key point is that exec can be any command. The npm package is just a way to carry a versioned identity; the mapper itself doesn't have to be Node.

2. Transport: JSON-RPC over STDIO, always driven by TypeScript

When module resolution finds a file with a registered extension, tsc starts the mapper as a child process and talks JSON-RPC over STDIO. TypeScript always sends the requests; the mapper only answers. There are just four methods.