Transpile TypeScript code to JavaScript with Closure annotations.
—
Generates Closure Compiler extern definitions from TypeScript ambient declarations and .d.ts files, enabling proper optimization of code that interacts with external libraries and APIs.
Core function that generates extern definitions from TypeScript source files.
/**
* Generates Closure Compiler extern definitions from ambient declarations
*/
function generateExterns(
typeChecker: ts.TypeChecker,
sourceFile: ts.SourceFile,
host: AnnotatorHost,
moduleResolutionHost: ts.ModuleResolutionHost,
options: ts.CompilerOptions
): { output: string; diagnostics: ts.Diagnostic[] };Usage Example:
import { generateExterns } from "tsickle";
const result = generateExterns(
typeChecker,
sourceFile,
host,
moduleResolutionHost,
compilerOptions
);
if (result.output) {
console.log("Generated externs:");
console.log(result.output);
}
if (result.diagnostics.length > 0) {
console.error("Externs generation warnings:", result.diagnostics);
}Function for combining and formatting multiple extern files with a standard header.
/**
* Concatenates externs with standardized header
*/
function getGeneratedExterns(
externs: { [fileName: string]: string },
rootDir: string
): string;Usage Example:
import { getGeneratedExterns } from "tsickle";
const externs = {
'src/types.d.ts': '/** @externs */ var MyGlobal;',
'src/lib.d.ts': '/** @externs */ var LibFunction;'
};
const combined = getGeneratedExterns(externs, '/project/src');
// Result includes header comment and all extern definitionsTsickle processes several types of ambient declarations:
// Global variable extern
declare var MY_GLOBAL: string;
// Generates: /** @externs */ var MY_GLOBAL;
// Global function extern
declare function myFunction(x: number): string;
// Generates: /** @externs */ function myFunction(number) {};
// Interface extern
declare interface Window {
customProperty: boolean;
}
// Generates: /** @type {boolean} */ Window.prototype.customProperty;
// Namespace extern
declare namespace MyLib {
function doSomething(): void;
const VERSION: string;
}
// Generates:
// /** @externs */
// var MyLib = {};
// /** @type {function(): void} */ MyLib.doSomething;
// /** @type {string} */ MyLib.VERSION;Tsickle handles different scoping contexts:
// types/global.d.ts
declare var jQuery: JQuery;
declare interface JQuery {
fadeIn(): JQuery;
}
// Generates global externs:
/** @externs */
var jQuery;
/** @record */
var JQuery = function() {};
/** @return {!JQuery} */ JQuery.prototype.fadeIn = function() {};// types/library.d.ts
export declare class LibraryClass {
method(): string;
}
// Generates namespaced externs based on module path:
/** @externs */
goog.module('types.library');
/** @constructor */
function LibraryClass() {}
/** @return {string} */ LibraryClass.prototype.method = function() {};
exports.LibraryClass = LibraryClass;For module-scoped externs, tsickle generates mangled names that match the file paths:
// File: src/vendor/external-lib.d.ts
export interface ExternalAPI {
call(): void;
}
// Generated extern with mangled name:
/** @externs */
var module$src$vendor$external_lib = {};
/** @record */
module$src$vendor$external_lib.ExternalAPI = function() {};
/** @return {void} */
module$src$vendor$external_lib.ExternalAPI.prototype.call = function() {};declare var str: string; // /** @type {string} */
declare var num: number; // /** @type {number} */
declare var bool: boolean; // /** @type {boolean} */
declare var any: any; // /** @type {?} */
declare var unknown: unknown; // /** @type {?} */declare var arr: string[]; // /** @type {!Array<string>} */
declare var obj: {x: number, y?: string}; // /** @type {{x: number, y: (string|undefined)}} */
declare var func: (x: string) => number; // /** @type {function(string): number} */
declare var union: string | number; // /** @type {(string|number)} */declare interface Container<T> {
value: T;
get(): T;
set(val: T): void;
}
// Generates:
/** @record @template T */
var Container = function() {};
/** @type {T} */ Container.prototype.value;
/** @return {T} */ Container.prototype.get = function() {};
/** @param {T} val */ Container.prototype.set = function(val) {};Generated externs tell Closure Compiler about external APIs:
// TypeScript source using extern
declare var gapi: {
load(api: string, callback: () => void): void;
};
// Usage in TypeScript
gapi.load('auth', () => {
console.log('Auth loaded');
});
// With generated extern, Closure Compiler knows:
// 1. gapi is external and shouldn't be renamed
// 2. gapi.load takes (string, function) parameters
// 3. The callback takes no parametersExterns prevent Closure Compiler from renaming external properties:
declare interface ExternalAPI {
importantMethod(): void;
}
// Without extern: obj.importantMethod() -> obj.a()
// With extern: obj.importantMethod() -> obj.importantMethod() (preserved)Extern generation is controlled through compiler options and host configuration:
interface AnnotatorHost {
/** Whether to generate untyped externs */
untyped?: boolean;
/** Paths to exclude from extern generation */
typeBlackListPaths?: Set<string>;
}
// Usage
const host: AnnotatorHost = {
pathToModuleName: (context, importPath) => importPath,
untyped: false, // Generate typed externs
typeBlackListPaths: new Set(['/node_modules/@types/node/']), // Skip Node.js types
};Common extern generation issues and their resolutions:
// Problem: Mapped types not supported in externs
declare type Partial<T> = {
[P in keyof T]?: T[P];
};
// Solution: Use concrete interfaces for externs
declare interface PartialUser {
name?: string;
age?: number;
}// Problem: Circular interface references
declare interface Node {
parent?: Node;
children: Node[];
}
// Tsickle handles this by generating forward declarations
/** @record */ var Node = function() {};
/** @type {Node|undefined} */ Node.prototype.parent;
/** @type {!Array<!Node>} */ Node.prototype.children;// Problem: Can't resolve ambient module
declare module 'external-lib' {
export function helper(): void;
}
// Solution: Ensure module resolution host can find the module
// or provide explicit path mappings in tsconfig.json// Good: Specific, focused extern files
// types/jquery.d.ts - Only jQuery types
// types/google-maps.d.ts - Only Google Maps types
// Avoid: Large, mixed extern files
// types/all-externals.d.ts - Everything mixed together// Good: Specific types
declare interface ApiResponse {
status: 'success' | 'error';
data: any;
}
// Avoid: Overly generic types
declare interface ApiResponse {
[key: string]: any;
}// Good: Include JSDoc for complex externs
/**
* External payment processing library
* @see https://docs.payment-lib.com/
*/
declare namespace PaymentLib {
/** Initialize the payment system */
function init(apiKey: string): void;
/** Process a payment transaction */
function charge(amount: number, token: string): Promise<PaymentResult>;
}Install with Tessl CLI
npx tessl i tessl/npm-tsickle