—declaration and —allowJs
The —declaration
flag in TypeScript allows us to generate .d.ts
files (declaration files) from TypeScript source files (i.e. .ts
and .tsx
files).These .d.ts
files are important for a couple of reasons.
First of all, they’re important because they allow TypeScript to type-check against other projects without re-checking the original source code.They’re also important because they allow TypeScript to interoperate with existing JavaScript libraries that weren’t built with TypeScript in mind.Finally, a benefit that is often underappreciated: both TypeScript and JavaScript users can benefit from these files when using editors powered by TypeScript to get things like better auto-completion.
Unfortunately, —declaration
didn’t work with the —allowJs
flag which allows mixing TypeScript and JavaScript input files.This was a frustrating limitation because it meant users couldn’t use the —declaration
flag when migrating codebases, even if they were JSDoc-annotated.TypeScript 3.7 changes that, and allows the two options to be used together!
The most impactful outcome of this feature might a bit subtle: with TypeScript 3.7, users can write libraries in JSDoc annotated JavaScript and support TypeScript users.
The way that this works is that when using allowJs
, TypeScript has some best-effort analyses to understand common JavaScript patterns; however, the way that some patterns are expressed in JavaScript don’t necessarily look like their equivalents in TypeScript.When declaration
emit is turned on, TypeScript figures out the best way to transform JSDoc comments and CommonJS exports into valid type declarations and the like in the output .d.ts
files.
As an example, the following code snippet
const assert = require("assert")
module.exports.blurImage = blurImage;
/**
* Produces a blurred image from an input buffer.
*
* @param input {Uint8Array}
* @param width {number}
* @param height {number}
*/
function blurImage(input, width, height) {
const numPixels = width * height * 4;
assert(input.length === numPixels);
const result = new Uint8Array(numPixels);
// TODO
return result;
}
Will produce a .d.ts
file like
/**
* Produces a blurred image from an input buffer.
*
* @param input {Uint8Array}
* @param width {number}
* @param height {number}
*/
export function blurImage(input: Uint8Array, width: number, height: number): Uint8Array;
This can go beyond basic functions with @param
tags too, where the following example:
/**
* @callback Job
* @returns {void}
*/
/** Queues work */
export class Worker {
constructor(maxDepth = 10) {
this.started = false;
this.depthLimit = maxDepth;
/**
* NOTE: queued jobs may add more items to queue
* @type {Job[]}
*/
this.queue = [];
}
/**
* Adds a work item to the queue
* @param {Job} work
*/
push(work) {
if (this.queue.length + 1 > this.depthLimit) throw new Error("Queue full!");
this.queue.push(work);
}
/**
* Starts the queue if it has not yet started
*/
start() {
if (this.started) return false;
this.started = true;
while (this.queue.length) {
/** @type {Job} */(this.queue.shift())();
}
return true;
}
}
will be transformed into the following .d.ts
file:
/**
* @callback Job
* @returns {void}
*/
/** Queues work */
export class Worker {
constructor(maxDepth?: number);
started: boolean;
depthLimit: number;
/**
* NOTE: queued jobs may add more items to queue
* @type {Job[]}
*/
queue: Job[];
/**
* Adds a work item to the queue
* @param {Job} work
*/
push(work: Job): void;
/**
* Starts the queue if it has not yet started
*/
start(): boolean;
}
export type Job = () => void;
Note that when using these flags together, TypeScript doesn’t necessarily have to downlevel .js
files.If you simply want TypeScript to create .d.ts
files, you can use the —emitDeclarationOnly
compiler option.
For more details, you can check out the original pull request.