🚀 Executive Summary
TL;DR: The TypeScript compiler (tsc) by default strips TSDoc comments from .d.ts declaration files, leading to a loss of crucial IntelliSense documentation for library consumers. This issue can be effectively resolved using three main approaches: the `removeComments: false` flag for quick internal fixes, Microsoft’s API Extractor for robust public library builds, or a custom build script for complex legacy edge cases.
🎯 Key Takeaways
- The TypeScript compiler’s default behavior is to strip TSDoc comments from .d.ts files to keep the output lean, as comments do not affect type-checking or final JavaScript output.
- Setting `removeComments: false` in `tsconfig.json` is a quick fix that preserves all comments, but it’s a blunt instrument that can expose internal notes and is generally unsuitable for public packages.
- API Extractor is the industry-standard solution for preserving TSDoc in .d.ts files for serious library development, offering robust control, type rollup into a single file, and API reporting to prevent breaking changes.
Struggling with TypeScript stripping TSDoc from your .d.ts declaration files? A veteran cloud architect breaks down why it happens and shares three battle-tested solutions to preserve your crucial documentation.
Hey, TypeScript! Where’d My Docs Go? Preserving TSDoc in .d.ts Files
I remember a Tuesday afternoon, about an hour before a major release. Our lead frontend dev, Sarah, DMs me in a panic: “Darian, the IntelliSense for our shared-utils library is gone. The types are there, but all the descriptions and @param notes vanished.” A quick check confirmed it. A junior dev had updated a dependency, and our entire IDE experience for a critical internal package was suddenly flying blind. We traced it back to a single, seemingly innocent change in the CI pipeline that rebuilt the package. The culprit? The TypeScript compiler doing exactly what it was told to do: compile code, not preserve comments.
So, Why Does This Even Happen?
This isn’t a bug; it’s a feature. Or at least, it’s the default behavior. The TypeScript compiler’s (tsc) primary mission is to transpile your TypeScript into JavaScript and generate type declaration files (.d.ts) that accurately represent the shape of your code. To tsc, comments and TSDoc are just noise—they don’t affect the type-checking or the final JavaScript output. So, to keep things lean and mean, it strips them out. It assumes another tool will handle the “documentation” part of the job. For a simple app, that’s fine. For a library author? It’s a nightmare.
Three Ways to Get Your Docs Back
Over the years, we’ve dealt with this on everything from small internal helpers to massive public-facing SDKs. Here are the three main approaches we take, from the quick-and-dirty to the architecturally sound.
Solution 1: The Quick Fix (The tsconfig.json Flag)
This is the fastest way to solve the problem, and it’s perfect for a quick internal project or when you’re just starting out. You’re telling tsc directly, “Hey, stop throwing my comments away.” You just need to tweak your tsconfig.json.
Inside your compilerOptions, set removeComments to false:
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"declaration": true,
"outDir": "./dist",
"removeComments": false, // <-- This is the magic flag
"strict": true
},
"include": ["src/**/*"]
}
Heads Up: This is a blunt instrument. It keeps all comments, including your internal
// TODO: Fix this laternotes. If you’re publishing a package to the public, this might expose things you don’t want seen. Use with caution.
Solution 2: The Permanent Fix (Using API Extractor)
When you’re serious about building a library, you need a serious tool. Enter API Extractor from Microsoft. This is the industry-standard way to solve this problem. Its entire purpose is to analyze the output from tsc and generate clean, consolidated, public-facing .d.ts files with all the TSDoc beautifully preserved.
The setup is more involved, but it’s worth it. You’ll add it as a dev dependency (npm install @microsoft/api-extractor --save-dev) and create a config file, typically api-extractor.json:
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/dist/index.d.ts",
"apiReport": {
"enabled": true,
"reportFolder": "<projectFolder>/etc/"
},
"docModel": {
"enabled": true
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>.d.ts"
},
"messages": {
"compilerMessageReporting": {
"default": {
"logLevel": "warning"
}
}
}
}
You then run it as a post-build step in your package.json. It gives you total control, rolls up all your types into a single file, and even generates an API report to prevent accidental breaking changes. This is how we build all our shared services at TechResolve.
Solution 3: The ‘Nuclear’ Option (A Custom Build Script)
Sometimes, you’re in a weird spot. API Extractor feels like overkill for a tiny internal tool, but the removeComments flag is too messy. I’ve been there. We had a legacy service, auth-helper-v1, that had a bizarre build process that broke API Extractor. The deadline was Friday. My solution? A hacky but effective shell script.
The idea is simple: let tsc do its thing, but then use another tool to stitch the TSDoc back in. This is a last resort, but it can save your skin.
Here’s a conceptual package.json script:
"scripts": {
"build:types": "tsc --emitDeclarationOnly",
"build:docs": "typedoc --out docs src/index.ts",
"build": "npm run build:types && node ./scripts/re-add-docs.js"
}
Your re-add-docs.js script would have to be clever. It might read the original .ts files, parse the TSDoc blocks using a regular expression or an AST parser, and then inject them back into the corresponding locations in the generated .d.ts files. It’s fragile and a pain to maintain, but when prod-db-01 is on fire and your team needs types now, you do what you have to do.
Warning: I am not proud of this solution, but I am proud that it worked. Only reach for this when the other two options are off the table. You’re creating technical debt, so document it well.
Wrapping Up: Choose the Right Tool for the Job
Here’s a quick breakdown to help you decide:
| Solution | Best For | Complexity |
1. removeComments: false |
Quick prototypes, small internal tools. | Low |
| 2. API Extractor | Public libraries, shared company packages, long-term projects. | Medium |
| 3. Custom Script | Legacy systems, emergency fixes, weird edge cases. | High |
Losing your documentation during compilation is a classic “rite of passage” for a TypeScript library author. It’s frustrating, but it forces you to think more deeply about your build process and what you’re actually shipping. Don’t just find a fix; find the right fix for your project’s scale and lifecycle. Your future self—and your teammates—will thank you.
🤖 Frequently Asked Questions
âť“ Why does TypeScript remove TSDoc comments from .d.ts files by default?
The TypeScript compiler (tsc) strips TSDoc comments by default because its primary mission is to transpile TypeScript into JavaScript and generate type declaration files that accurately represent the *shape* of the code. Comments are considered noise that don’t affect type-checking or the final JavaScript output, so they are removed for a lean output.
âť“ How do the `removeComments` flag and API Extractor compare for preserving TSDoc in libraries?
The `removeComments: false` flag is a quick, low-complexity solution that preserves *all* comments, including internal ones, making it suitable for quick prototypes or small internal tools. API Extractor, conversely, is a medium-complexity, industry-standard tool designed for public libraries, providing fine-grained control, TSDoc preservation, type rollup into a single file, and API reporting without exposing internal development notes.
âť“ What is a common pitfall when using `removeComments: false` for TSDoc preservation?
A common pitfall when using `removeComments: false` is deploying a public package where it exposes *all* comments, including internal development notes like `// TODO: Fix this later`. This can reveal unintended information to consumers and is generally not recommended for public-facing libraries, creating technical debt if not managed.
Leave a Reply