CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tsickle

Transpile TypeScript code to JavaScript with Closure annotations.

Pending
Overview
Eval results
Files

externs-generation.mddocs/

Externs Generation

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.

Capabilities

Main Externs Generation

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);
}

Externs Consolidation

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 definitions

Extern Generation Process

Ambient Declaration Processing

Tsickle processes several types of ambient declarations:

  1. Global declarations - Variables and functions in global scope
  2. Module declarations - Ambient module definitions
  3. Interface declarations - Type definitions for external objects
  4. Namespace declarations - Nested namespace structures
  5. Type-only imports - Import statements used only for types

Declaration Types

// 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;

Module Scoping

Global vs Module Externs

Tsickle handles different scoping contexts:

Global Externs (.d.ts files without exports)

// 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() {};

Module Externs (.d.ts files with exports)

// 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;

Mangled Names

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() {};

Type Mapping in Externs

Primitive Types

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 {?} */

Complex Types

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)} */

Generic Types

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) {};

Integration with Closure Compiler

Extern Usage

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 parameters

Property Preservation

Externs 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)

Configuration

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
};

Error Handling

Common extern generation issues and their resolutions:

Unsupported Type Constructs

// 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;
}

Circular References

// 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;

Module Resolution Issues

// 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

Best Practices

Extern Organization

// 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

Type Specificity

// Good: Specific types
declare interface ApiResponse {
  status: 'success' | 'error';
  data: any;
}

// Avoid: Overly generic types  
declare interface ApiResponse {
  [key: string]: any;
}

Documentation

// 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

docs

core-transformation.md

decorator-support.md

externs-generation.md

index.md

jsdoc-processing.md

module-system.md

path-utilities.md

transformer-utilities.md

type-translation.md

tile.json