A few lines.
Then ship your types.
Start with the CLI. Reach for the TypeScript API when your build needs more control.
Install dtsx.
Add dtsx to your library with npm, or the package manager you already use (bun add -d @dtsx/dtsx, pnpm add -D @dtsx/dtsx).
This adds the dtsx executable to your project. The CLI runs on Bun. Commit your lockfile to keep builds reproducible.
Your first declarations.
Run the installed CLI from your project directory.
bunx --no-install dtsx generateBy default, dtsx reads your source directory and writes declarations to dist. Set the root, entrypoints, and output location in a config file.
For an existing build script, append dtsx generate after your JavaScript build. Keep a separate TypeScript check in CI.
Give your types a home.
Create dts.config.ts in your project root. This example bundles the public entry into one declaration file.
import { defineConfig } from '@dtsx/dtsx'
export default defineConfig({
root: './src',
entrypoints: ['index.ts'],
outdir: './dist/types',
bundle: true,
bundleOutput: 'index.d.ts',
keepComments: true,
})Set bundle: false to emit per-file declarations. Options also include watch, incremental, declarationMap, and exclude. Check the configuration reference for the full surface.
About isolated declarations
dtsx can generate declarations with or without TypeScript's isolatedDeclarations option. Explicit public annotations make your intended API clear and enable annotation-first generation. The benchmarks include both explicit annotations and a separate workload with omitted types, inferred returns, and imported values. Compare the fixture workloads and their qualification results before choosing a mode.
Make it part of your build.
Call generate() from TypeScript and check its result before continuing your release.
import { generate } from '@dtsx/dtsx'
const stats = await generate({
root: './src',
entrypoints: ['index.ts'],
outdir: './dist/types',
keepComments: true,
})
if (stats.filesFailed > 0) {
throw new Error('Declaration generation failed')
}The same config file is available to the library API. Explicit options let you keep build-specific behavior close to the build script.
Bring your bundler.
Each build tool has its own plugin, published as @dtsx/<tool>. Install the one you use next to @dtsx/dtsx:
npm install -D @dtsx/viteThen add it to your config:
import { defineConfig } from 'vite'
import { dts } from '@dtsx/vite'
export default defineConfig({
plugins: [dts({
root: './src',
outdir: './dist/types',
})],
})The tsup plugin supports tsup >=8.3.6 <8.5.1 and requires @swc/core.
Point consumers at your types.
Set your package's types field to its generated entry. If you use an exports map, add the matching types condition.
{
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/index.js"
}
}
}Test a packed installation with a strict consumer before publishing. Declaration generation and a full semantic typecheck do different work.
Ask a question or report an issue ↗