0
# Externs Generation
1
2
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.
3
4
## Capabilities
5
6
### Main Externs Generation
7
8
Core function that generates extern definitions from TypeScript source files.
9
10
```typescript { .api }
11
/**
12
* Generates Closure Compiler extern definitions from ambient declarations
13
*/
14
function generateExterns(
15
typeChecker: ts.TypeChecker,
16
sourceFile: ts.SourceFile,
17
host: AnnotatorHost,
18
moduleResolutionHost: ts.ModuleResolutionHost,
19
options: ts.CompilerOptions
20
): { output: string; diagnostics: ts.Diagnostic[] };
21
```
22
23
**Usage Example:**
24
25
```typescript
26
import { generateExterns } from "tsickle";
27
28
const result = generateExterns(
29
typeChecker,
30
sourceFile,
31
host,
32
moduleResolutionHost,
33
compilerOptions
34
);
35
36
if (result.output) {
37
console.log("Generated externs:");
38
console.log(result.output);
39
}
40
41
if (result.diagnostics.length > 0) {
42
console.error("Externs generation warnings:", result.diagnostics);
43
}
44
```
45
46
### Externs Consolidation
47
48
Function for combining and formatting multiple extern files with a standard header.
49
50
```typescript { .api }
51
/**
52
* Concatenates externs with standardized header
53
*/
54
function getGeneratedExterns(
55
externs: { [fileName: string]: string },
56
rootDir: string
57
): string;
58
```
59
60
**Usage Example:**
61
62
```typescript
63
import { getGeneratedExterns } from "tsickle";
64
65
const externs = {
66
'src/types.d.ts': '/** @externs */ var MyGlobal;',
67
'src/lib.d.ts': '/** @externs */ var LibFunction;'
68
};
69
70
const combined = getGeneratedExterns(externs, '/project/src');
71
// Result includes header comment and all extern definitions
72
```
73
74
## Extern Generation Process
75
76
### Ambient Declaration Processing
77
78
Tsickle processes several types of ambient declarations:
79
80
1. **Global declarations** - Variables and functions in global scope
81
2. **Module declarations** - Ambient module definitions
82
3. **Interface declarations** - Type definitions for external objects
83
4. **Namespace declarations** - Nested namespace structures
84
5. **Type-only imports** - Import statements used only for types
85
86
### Declaration Types
87
88
```typescript
89
// Global variable extern
90
declare var MY_GLOBAL: string;
91
// Generates: /** @externs */ var MY_GLOBAL;
92
93
// Global function extern
94
declare function myFunction(x: number): string;
95
// Generates: /** @externs */ function myFunction(number) {};
96
97
// Interface extern
98
declare interface Window {
99
customProperty: boolean;
100
}
101
// Generates: /** @type {boolean} */ Window.prototype.customProperty;
102
103
// Namespace extern
104
declare namespace MyLib {
105
function doSomething(): void;
106
const VERSION: string;
107
}
108
// Generates:
109
// /** @externs */
110
// var MyLib = {};
111
// /** @type {function(): void} */ MyLib.doSomething;
112
// /** @type {string} */ MyLib.VERSION;
113
```
114
115
## Module Scoping
116
117
### Global vs Module Externs
118
119
Tsickle handles different scoping contexts:
120
121
#### Global Externs (.d.ts files without exports)
122
123
```typescript
124
// types/global.d.ts
125
declare var jQuery: JQuery;
126
declare interface JQuery {
127
fadeIn(): JQuery;
128
}
129
130
// Generates global externs:
131
/** @externs */
132
var jQuery;
133
/** @record */
134
var JQuery = function() {};
135
/** @return {!JQuery} */ JQuery.prototype.fadeIn = function() {};
136
```
137
138
#### Module Externs (.d.ts files with exports)
139
140
```typescript
141
// types/library.d.ts
142
export declare class LibraryClass {
143
method(): string;
144
}
145
146
// Generates namespaced externs based on module path:
147
/** @externs */
148
goog.module('types.library');
149
/** @constructor */
150
function LibraryClass() {}
151
/** @return {string} */ LibraryClass.prototype.method = function() {};
152
exports.LibraryClass = LibraryClass;
153
```
154
155
### Mangled Names
156
157
For module-scoped externs, tsickle generates mangled names that match the file paths:
158
159
```typescript
160
// File: src/vendor/external-lib.d.ts
161
export interface ExternalAPI {
162
call(): void;
163
}
164
165
// Generated extern with mangled name:
166
/** @externs */
167
var module$src$vendor$external_lib = {};
168
/** @record */
169
module$src$vendor$external_lib.ExternalAPI = function() {};
170
/** @return {void} */
171
module$src$vendor$external_lib.ExternalAPI.prototype.call = function() {};
172
```
173
174
## Type Mapping in Externs
175
176
### Primitive Types
177
178
```typescript
179
declare var str: string; // /** @type {string} */
180
declare var num: number; // /** @type {number} */
181
declare var bool: boolean; // /** @type {boolean} */
182
declare var any: any; // /** @type {?} */
183
declare var unknown: unknown; // /** @type {?} */
184
```
185
186
### Complex Types
187
188
```typescript
189
declare var arr: string[]; // /** @type {!Array<string>} */
190
declare var obj: {x: number, y?: string}; // /** @type {{x: number, y: (string|undefined)}} */
191
declare var func: (x: string) => number; // /** @type {function(string): number} */
192
declare var union: string | number; // /** @type {(string|number)} */
193
```
194
195
### Generic Types
196
197
```typescript
198
declare interface Container<T> {
199
value: T;
200
get(): T;
201
set(val: T): void;
202
}
203
204
// Generates:
205
/** @record @template T */
206
var Container = function() {};
207
/** @type {T} */ Container.prototype.value;
208
/** @return {T} */ Container.prototype.get = function() {};
209
/** @param {T} val */ Container.prototype.set = function(val) {};
210
```
211
212
## Integration with Closure Compiler
213
214
### Extern Usage
215
216
Generated externs tell Closure Compiler about external APIs:
217
218
```typescript
219
// TypeScript source using extern
220
declare var gapi: {
221
load(api: string, callback: () => void): void;
222
};
223
224
// Usage in TypeScript
225
gapi.load('auth', () => {
226
console.log('Auth loaded');
227
});
228
229
// With generated extern, Closure Compiler knows:
230
// 1. gapi is external and shouldn't be renamed
231
// 2. gapi.load takes (string, function) parameters
232
// 3. The callback takes no parameters
233
```
234
235
### Property Preservation
236
237
Externs prevent Closure Compiler from renaming external properties:
238
239
```typescript
240
declare interface ExternalAPI {
241
importantMethod(): void;
242
}
243
244
// Without extern: obj.importantMethod() -> obj.a()
245
// With extern: obj.importantMethod() -> obj.importantMethod() (preserved)
246
```
247
248
## Configuration
249
250
Extern generation is controlled through compiler options and host configuration:
251
252
```typescript
253
interface AnnotatorHost {
254
/** Whether to generate untyped externs */
255
untyped?: boolean;
256
/** Paths to exclude from extern generation */
257
typeBlackListPaths?: Set<string>;
258
}
259
260
// Usage
261
const host: AnnotatorHost = {
262
pathToModuleName: (context, importPath) => importPath,
263
untyped: false, // Generate typed externs
264
typeBlackListPaths: new Set(['/node_modules/@types/node/']), // Skip Node.js types
265
};
266
```
267
268
## Error Handling
269
270
Common extern generation issues and their resolutions:
271
272
### Unsupported Type Constructs
273
274
```typescript
275
// Problem: Mapped types not supported in externs
276
declare type Partial<T> = {
277
[P in keyof T]?: T[P];
278
};
279
280
// Solution: Use concrete interfaces for externs
281
declare interface PartialUser {
282
name?: string;
283
age?: number;
284
}
285
```
286
287
### Circular References
288
289
```typescript
290
// Problem: Circular interface references
291
declare interface Node {
292
parent?: Node;
293
children: Node[];
294
}
295
296
// Tsickle handles this by generating forward declarations
297
/** @record */ var Node = function() {};
298
/** @type {Node|undefined} */ Node.prototype.parent;
299
/** @type {!Array<!Node>} */ Node.prototype.children;
300
```
301
302
### Module Resolution Issues
303
304
```typescript
305
// Problem: Can't resolve ambient module
306
declare module 'external-lib' {
307
export function helper(): void;
308
}
309
310
// Solution: Ensure module resolution host can find the module
311
// or provide explicit path mappings in tsconfig.json
312
```
313
314
## Best Practices
315
316
### Extern Organization
317
318
```typescript
319
// Good: Specific, focused extern files
320
// types/jquery.d.ts - Only jQuery types
321
// types/google-maps.d.ts - Only Google Maps types
322
323
// Avoid: Large, mixed extern files
324
// types/all-externals.d.ts - Everything mixed together
325
```
326
327
### Type Specificity
328
329
```typescript
330
// Good: Specific types
331
declare interface ApiResponse {
332
status: 'success' | 'error';
333
data: any;
334
}
335
336
// Avoid: Overly generic types
337
declare interface ApiResponse {
338
[key: string]: any;
339
}
340
```
341
342
### Documentation
343
344
```typescript
345
// Good: Include JSDoc for complex externs
346
/**
347
* External payment processing library
348
* @see https://docs.payment-lib.com/
349
*/
350
declare namespace PaymentLib {
351
/** Initialize the payment system */
352
function init(apiKey: string): void;
353
354
/** Process a payment transaction */
355
function charge(amount: number, token: string): Promise<PaymentResult>;
356
}
357
```