or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-transformation.mddecorator-support.mdexterns-generation.mdindex.mdjsdoc-processing.mdmodule-system.mdpath-utilities.mdtransformer-utilities.mdtype-translation.md

externs-generation.mddocs/

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

```