Your code stays yours.
Emit declarations from TypeScript, TSX, Vue, and STX. Keep the comments that make your public API useful.
The narrowest declarations, with your default values in every tooltip. Generate .d.ts files with Bun, from your first export to your next release.
export declare
function greet(options: Options): string;Keep writing your library. Let dtsx handle the declarations.
Emit declarations from TypeScript, TSX, Vue, and STX. Keep the comments that make your public API useful.
Mirror your source files or bundle declarations into one public entry.
Watch mode and incremental generation keep declaration work in your development flow.
Explore configurationdtsx infers the narrowest sound type from each value, with no compiler and no annotations required. Then it keeps the value itself as @defaultValue, so your users see it in every editor tooltip.
export const config = {
port: 3000,
host: 'localhost',
retries: 3,
}
export const LOG_LEVELS = ['debug', 'info', 'warn'] as const
export let mode = 'production'
export function connect(host = 'localhost', port = 5432) {
return { host, port, secure: port === 443 }
}
export class Client {
timeout = 1000
static version = '1.0.0'
}export declare const config: {
port: number;
host: string;
retries: number;
};
export declare const LOG_LEVELS: readonly ["debug", "info", "warn"];
export declare let mode: string;
export declare function connect(host?: string, port?: number): {
host: string;
port: number;
secure: boolean;
};
export declare class Client {
timeout: number;
static version: string;
}export declare function connect(host?: string, port?: number): {
host: string;
port: number;
secure: boolean
};
/** @defaultValue `{ port: 3000, host: 'localhost', retries: 3 }` */
export declare const config: {
/** @defaultValue 3000 */
port: number;
/** @defaultValue 'localhost' */
host: string;
/** @defaultValue 3 */
retries: number
};
export declare const LOG_LEVELS: readonly ['debug', 'info', 'warn'];
/** @defaultValue 'production' */
export declare let mode: string;
export declare class Client {
/** @defaultValue 1000 */
timeout: number;
/** @defaultValue '1.0.0' */
static version: string;
}
as const values keep their literal types, like readonly ['debug', 'info', 'warn'].3000, not just number.Readable declarations, generated from real source. This example is emitted by dtsx when the website builds.
Meet the TypeScript APIexport interface Options {
name: string
retries?: number
}
export function greet(options: Options): string {
return `Hello, ${options.name}`
}export declare function greet(options: Options): string;
export declare interface Options {
name: string
retries?: number
}
Fresh processes. Identical inputs. Declarations checked after every run.
Nine measured samples per tool.
Median wall-clock time, including startup.
| Tool | Median |
|---|---|
| dtsx bundle dtsx | 147.6 ms |
| tsdown / Oxc | 244.6 ms |
| tsdown / TypeScript | 883.7 ms |
| tsup | 1162.3 ms |
| tsc + rollup-plugin-dts | 1666.7 ms |
| dts-bundle-generator | 2055.4 ms |
| Vite + unplugin-dts | 2361.9 ms |
| tsc + API Extractor | 2600.1 ms |
Apple M1 (Virtual) · Bun 1.4.4 · November 8, 2026. This table uses explicitly annotated libraries. The full lab also tests unannotated inference on macOS and Linux. Tools perform different amounts of checking and bundling. For 100 modules, Oxc has the lowest median in the separate per-file emission workload. Read the methodology.
Use the CLI on its own or add declaration generation to your build.
Free to use. Open to contributions.