Declaration Files (dts)
TypeScript declaration file generation and isolated declarations in Packem
Declaration Files (dts)
Packem provides comprehensive TypeScript declaration file generation with support for both traditional and isolated declarations. Declaration files (.d.ts) are essential for TypeScript libraries to provide type information to consumers.
Automatic Detection
Packem automatically enables declaration file generation when it detects TypeScript usage in your project:
- TypeScript files (
.ts,.tsx) in your source code typesortypingsfield in yourpackage.jsontsconfig.jsonfile in your project
{
"name": "my-library",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}Configuration
Enable/Disable Declaration Generation
import { defineConfig } from '@visulima/packem/config'
export default defineConfig({
// Explicitly enable declaration generation
declaration: true,
transformer: 'esbuild'
})Advanced Declaration Options
export default defineConfig({
declaration: {
// Generate declaration files
enabled: true,
// Output directory for declarations
outDir: 'types',
// Generate declaration maps for better debugging
declarationMap: true,
// Include comments in declaration files
removeComments: false,
// Emit declarations only (no JS output)
emitDeclarationOnly: false
}
})Isolated Declarations
Isolated declarations are a TypeScript 5.5+ feature that dramatically improves declaration generation performance by avoiding expensive type inference.
Isolated declarations can be 10-100x faster than traditional declaration generation for large projects.
Enable Isolated Declarations
import { defineConfig } from '@visulima/packem/config'
import isolatedDeclarations from '@visulima/packem/dts/isolated/transformer/typescript'
export default defineConfig({
isolatedDeclarationTransformer: isolatedDeclarations,
transformer: 'esbuild'
})Available Isolated Declaration Transformers
import isolatedDeclarations from '@visulima/packem/dts/isolated/transformer/typescript'
export default defineConfig({
isolatedDeclarationTransformer: isolatedDeclarations,
transformer: 'esbuild'
})import isolatedDeclarations from '@visulima/packem/dts/isolated/transformer/swc'
export default defineConfig({
isolatedDeclarationTransformer: isolatedDeclarations,
transformer: 'swc'
})import isolatedDeclarations from '@visulima/packem/dts/isolated/transformer/oxc'
export default defineConfig({
isolatedDeclarationTransformer: isolatedDeclarations,
transformer: 'oxc'
})Isolated Declarations Requirements
For isolated declarations to work, your TypeScript code must follow certain patterns:
// ✅ Good - Explicit return types
export function greet(name: string): string {
return `Hello, ${name}!`
}
// ✅ Good - Explicit interface
export interface User {
id: number
name: string
}
// ❌ Bad - Inferred return type
export function greet(name: string) {
return `Hello, ${name}!`
}
// ❌ Bad - Inferred type
export const config = {
apiUrl: 'https://api.example.com'
}
// ✅ Good - Explicit type
export const config: { apiUrl: string } = {
apiUrl: 'https://api.example.com'
}Isolated declarations require explicit type annotations on all exported declarations. This is a TypeScript compiler requirement, not a Packem limitation.
JSDoc comments and isolated declarations: OXC and swc do not preserve JSDoc comments when generating .d.ts files via isolated declarations. If your library's API documentation relies on JSDoc comments in declaration files, use the TypeScript isolated declarations transformer or disable isolated declarations entirely (which uses tsc under the hood). Also ensure removeComments is not set to true in your tsconfig.json.
Declaration File Formats
Packem generates declaration files that match your output formats:
ESM/CJS Dual Package
{
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
}
}Packem will generate:
dist/index.d.mts- ESM declaration filedist/index.d.cts- CJS declaration file
Legacy Node.js Compatibility
export default defineConfig({
rollup: {
node10Compatibility: {
typeScriptVersion: '>=5.0',
writeToPackageJson: true
}
}
})This generates typeVersions in your package.json for Node.js 10 compatibility:
{
"types": "./dist/index.d.ts",
"typesVersions": {
">=5.0": {
"*": ["./dist/index.d.ts"]
}
}
}Declaration Bundling
Bundle multiple declaration files into a single file:
export default defineConfig({
declaration: {
enabled: true,
bundle: true, // Bundle all declarations into one file
outFile: 'dist/index.d.ts'
}
})Declaration Bundling with rollup-plugin-dts
import dts from 'rollup-plugin-dts'
export default defineConfig({
rollup: {
plugins: [
dts({
// Bundle all .d.ts files
bundledPackages: ['my-dependency'],
// Respect external dependencies
respectExternal: true
})
]
}
})Include/Exclude Filters
Control which source files have .d.ts declarations generated using include and exclude patterns.
This is useful when you want to skip declaration generation for specific files (e.g., test utilities, internal helpers).
export default defineConfig({
declaration: true,
rollup: {
dts: {
// Only generate declarations for files in src/
include: ['src/**/*.ts'],
// Skip test files and internal modules
exclude: ['src/**/*.test.ts', 'src/internal/**']
}
}
})Patterns accept minimatch glob strings, regular expressions, or arrays of either. When neither include nor exclude is specified, all source files are processed.
Inlining Types from External Packages
Packem automatically inlines types from optional peer dependencies and optional dependencies into your .d.ts output, while keeping them external for JS. This prevents TS2307 errors for consumers who don't install all your optional peers.
Automatic Behavior
Types are automatically inlined for:
- Packages in
optionalDependencies - Packages in
peerDependenciesthat are marked as optional inpeerDependenciesMeta
{
"peerDependencies": {
"webpack": "^5.0.0",
"vite": "^5.0.0",
"react": "^18.0.0"
},
"peerDependenciesMeta": {
"webpack": { "optional": true },
"vite": { "optional": true }
}
}With this config, types from webpack and vite are automatically inlined in .d.ts output (since they're optional), while react stays external (since it's a required peer).
Manual Configuration
Use rollup.dts.resolve to add extra packages or override the automatic behavior:
export default defineConfig({
rollup: {
dts: {
// Add extra packages on top of auto-detected ones
resolve: ['some-extra-package', /^@farmfe\//]
}
}
})Options:
true— inline types from all node_modulesfalse— disable auto-resolution, keep all dependencies external in.d.ts(string | RegExp)[]— merged with auto-detected optional peers/deps
Multiple Entry Points
Generate declarations for multiple entry points:
export default defineConfig({
entry: {
index: 'src/index.ts',
utils: 'src/utils.ts',
cli: 'src/cli.ts'
},
declaration: true
})This generates:
dist/index.d.tsdist/utils.d.tsdist/cli.d.ts
Declaration Maps
Enable declaration maps for better IDE support:
export default defineConfig({
declaration: {
enabled: true,
declarationMap: true
}
})Declaration maps allow IDEs to navigate from compiled declaration files back to the original TypeScript source.
External Dependencies
Handle external dependencies in declarations:
export default defineConfig({
rollup: {
external: ['react', 'lodash'],
// Ensure externals are preserved in declarations
dts: {
external: ['react', 'lodash']
}
}
})Path Mapping
Resolve TypeScript path mappings in declarations:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@utils/*": ["src/utils/*"]
}
}
}// src/index.ts
import { helper } from '@utils/helper'
import { Component } from '@/components/Component'Packem automatically resolves these paths in the generated declarations.
Validation
Validate your declaration files with external tools:
export default defineConfig({
declaration: true,
hooks: {
'build:after': async () => {
// Run attw (Are The Types Wrong?)
await import('@arethetypeswrong/core').then(({ analyze }) => {
return analyze('./package.json')
})
}
}
})Performance Optimization
Use Isolated Declarations
For maximum performance, use isolated declarations:
export default defineConfig({
// Use isolated declarations for speed
isolatedDeclarationTransformer: isolatedDeclarations,
// Use fast transformer for JavaScript
transformer: 'esbuild'
})Incremental Compilation
Enable TypeScript incremental compilation:
// tsconfig.json
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo"
}
}Parallel Processing
Process multiple entry points in parallel:
export default defineConfig({
entry: {
index: 'src/index.ts',
utils: 'src/utils.ts',
cli: 'src/cli.ts'
},
declaration: {
enabled: true,
parallel: true // Process declarations in parallel
}
})Troubleshooting
Declaration Generation Fails
- Check TypeScript version: Ensure you're using TypeScript 4.5 or later
- Verify tsconfig.json: Ensure your TypeScript configuration is valid
- Check for type errors: Fix any TypeScript compilation errors
- Use explicit types: For isolated declarations, ensure all exports have explicit types
Missing Types
// Add explicit return type
export function myFunction(): string {
return 'hello'
}
// Add explicit interface
export interface MyConfig {
apiUrl: string
}Path Resolution Issues
Ensure your tsconfig.json paths are correctly configured:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*"]
}Missing JSDoc Comments in Declaration Files
If JSDoc comments are missing from your generated .d.ts files:
- Check
removeComments: EnsureremoveCommentsis nottruein yourtsconfig.json. TypeScript strips all comments (including JSDoc) from declaration output when this is enabled. - Check your declaration transformer: OXC and swc isolated declaration transformers do not preserve JSDoc comments. Switch to the TypeScript transformer or disable isolated declarations.
// tsconfig.json
{
"compilerOptions": {
"removeComments": false
}
}Performance Issues
- Use isolated declarations for large projects
- Enable incremental compilation in TypeScript
- Exclude test files from declaration generation using
rollup.dts.exclude - Use declaration bundling to reduce file count
Examples
Basic Library
export default defineConfig({
transformer: 'esbuild',
declaration: true
})React Component Library
export default defineConfig({
transformer: 'esbuild',
esbuild: {
jsx: 'automatic',
jsxImportSource: 'react'
},
declaration: {
enabled: true,
declarationMap: true
},
rollup: {
external: ['react', 'react-dom']
}
})High-Performance Library
import isolatedDeclarations from '@visulima/packem/dts/isolated/transformer/typescript'
export default defineConfig({
isolatedDeclarationTransformer: isolatedDeclarations,
transformer: 'esbuild',
declaration: {
enabled: true,
parallel: true
}
})Known Limitations
Isolated Declarations (OXC / swc)
- JSDoc comments are not preserved. OXC and swc strip all comments during isolated declaration generation. Use the TypeScript transformer or disable isolated declarations if your library relies on JSDoc in
.d.tsfiles. - Triple-slash directives are not preserved.
/// <reference types="..." />directives in source files are stripped by OXC and swc. They are preserved when using thedtsInputmode with pre-generated.d.tsfiles.
TypeScript Compiler (tsc)
- JSDoc comments are stripped when
removeComments: truein yourtsconfig.json. Ensure this is set tofalseif you want JSDoc comments in your declaration output. - Triple-slash directives are not always emitted. TypeScript only includes
/// <reference>directives in.d.tsoutput when it determines they are necessary for the output types. Source-level directives may be omitted.
DTS Bundling
- Namespace imports for complex types. When inlining types from external packages via
rollup.dts.resolve, packages with complex type dependencies may produce namespace imports (import * as pkg from 'pkg') in the output rather than fully inlining all type definitions. @babel/generatoris used for code generation. Any upstream Babel bugs with TypeScript AST nodes may affect the bundled.d.tsoutput.
Related Options
- Transformers - Choose compatible transformers
- Output Format - Match declaration formats to output formats
- TSConfig - TypeScript configuration integration
- Package Exports - Automatic exports generation