Externs and Exports

Purpose of Externs

Externs are declarations that tell Closure Compiler the names of symbols that should not be renamed during advanced compilation. They are called externs because these symbols are most often defined by code outside the compilation, such a native code, or third-party libraries. For this reason, externs often also have type annotations, so that Closure Compiler can typecheck your use of those symbols.

In general, it is best to think of externs as an API contract between the implementor and the consumers of some piece of compiled code. The externs define what the implementor promises to supply, and what the consumers can depend on using. Both sides need a copy of the contract.

Externs are similar to header files in other languages.

Externs Syntax

Externs are files that look very much like normal JavaScript annotated for Closure Compiler. The main difference is that their contents are never printed as part of the compiled output, so none of the values are meaningful, only the names and types.

Below is an example of an externs file for a simple library.

 // 
  
 The 
  
 `@ 
 externs 
 ` 
  
 annotation 
  
 is 
  
 the 
  
 best 
  
 way 
  
 to 
  
 indicate 
  
 a 
  
 file 
  
 contains 
  
 externs 
 . 
 /** 
  
 * 
  
 @ 
 fileoverview 
  
 Public 
  
 API 
  
 of 
  
 my_math 
 . 
 js 
 . 
  
 * 
  
 @ 
 externs 
  
 */ 
 // 
  
 Externs 
  
 often 
  
 declare 
  
 global 
  
 namespaces 
 . 
 const 
  
 myMath 
  
 = 
  
 {}; 
 // 
  
 Externs 
  
 can 
  
 declare 
  
 functions 
 , 
  
 most 
  
 importantly 
  
 their 
  
 names 
 . 
 /** 
  
 * 
  
 @ 
 param 
  
 { 
 number 
 } 
  
 x 
  
 * 
  
 @ 
 param 
  
 { 
 number 
 } 
  
 y 
  
 * 
  
 @ 
 return 
  
 { 
 ! 
 myMath 
 . 
 DivResult 
 } 
  
 */ 
 myMath 
 . 
 div 
  
 = 
  
 function 
 ( 
 x 
 , 
  
 y 
 ) 
  
 {}; 
  
 // 
  
 Note 
  
 the 
  
 empty 
  
 body 
 . 
 // 
  
 Externs 
  
 can 
  
 contain 
  
 type 
  
 declarations 
 , 
  
 such 
  
 as 
  
 classes 
  
 and 
  
 interfaces 
 . 
 /** 
  
 The 
  
 result 
  
 of 
  
 an 
  
 integer 
  
 division 
 . 
  
 */ 
 myMath 
 . 
 DivResult 
  
 = 
  
 class 
  
 { 
  
 // 
  
 Constructors 
  
 are 
  
 special 
 ; 
  
 member 
  
 fields 
  
 can 
  
 be 
  
 declared 
  
 in 
  
 their 
  
 bodies 
 . 
  
 constructor 
 () 
  
 { 
  
 /** 
  
 @ 
 type 
  
 { 
 number 
 } 
  
 */ 
  
 this 
 . 
 quotient 
 ; 
  
 /** 
  
 @ 
 type 
  
 { 
 number 
 } 
  
 */ 
  
 this 
 . 
 remainder 
 ; 
  
 } 
  
 // 
  
 Methods 
  
 can 
  
 be 
  
 declared 
  
 as 
  
 usual 
 ; 
  
 their 
  
 bodies 
  
 are 
  
 meaningless 
  
 though 
 . 
  
 /** 
  
 @ 
 return 
  
 { 
 ! 
 Array<number> 
 } 
  
 */ 
  
 toPair 
 () 
  
 {} 
 }; 
 // 
  
 Fields 
  
 and 
  
 methods 
  
 can 
  
 also 
  
 be 
  
 declared 
  
 using 
  
 prototype 
  
 notation 
 . 
 /** 
  
 * 
  
 @ 
 override 
  
 * 
  
 @ 
 param 
  
 { 
 number 
 = 
 } 
  
 radix 
  
 */ 
 myMath 
 . 
 DivResult 
 . 
 prototype 
 . 
 toString 
  
 = 
  
 function 
 ( 
 radix 
 ) 
  
 {}; 
  

The --externs Flag

Generally, the @externs annotation is the best way to inform the compiler that a file contains externs. Such files can be included as normal source files using the --js command-line flag,

However, there is another, older way, to specify externs files. The --externs command-line flag can be used to pass externs files explicitly. This method is not recommended.

Using Externs

The externs from above can be consumed as follows.

 /** 
 * @fileoverview Do some math. 
 */ 
 /** 
 * @param {number} x 
 * @param {number} y 
 * @return {number} 
 */ 
 export 
  
 function 
  
 greatestCommonDivisor 
 ( 
 x 
 , 
  
 y 
 ) 
  
 { 
  
 while 
  
 ( 
 y 
  
 != 
  
 0 
 ) 
  
 { 
  
 const 
  
 temp 
  
 = 
  
 y 
 ; 
  
 // 
  
 `myMath` 
  
 is 
  
 a 
  
 global 
 , 
  
 it 
  
 and 
  
 `myMath.div` 
  
 are 
  
 never 
  
 renamed 
 . 
  
 const 
  
 result 
  
 = 
  
 myMath 
 . 
 div 
 ( 
 x 
 , 
  
 y 
 ); 
  
 // 
  
 `remainder` 
  
 is 
  
 also 
  
 never 
  
 renamed 
  
 on 
  
 instances 
  
 of 
  
 `DivResult` 
 . 
  
 y 
  
 = 
  
 result 
 . 
 remainder 
 ; 
  
 x 
  
 = 
  
 temp 
 ; 
  
 } 
  
 return 
  
 x 
 ; 
 } 
  

Purpose of Exports

Exports are another mechanism for giving symbols consistent names after compilation. They are less useful than externs and often confusing. For all but simple cases they are best avoided.

Exports rely on the fact that Closure Compiler doesn't modify string literals. By assigning an object to a property named using a literal, the object will be available through that property name even after compilation.

Below is a simple example.

 /** 
 * @fileoverview Do some math. 
 */ 
 // 
  
 Note 
  
 that 
  
 the 
  
 concept 
  
 of 
  
 module 
  
 exports 
  
 is 
  
 totally 
  
 unrelated 
 . 
 /** @return {number} */ 
 export 
  
 function 
  
 myFunction 
 () 
  
 { 
  
 return 
  
 5 
 ; 
 } 
 // 
  
 This 
  
 assignment 
  
 ensures 
  
 `myFunctionAlias` 
  
 will 
  
 be 
  
 a 
  
 global 
  
 alias 
  
 exposing 
  
 `myFunction` 
 , 
 // 
  
 even 
  
 after 
  
 compilation 
 . 
 window 
 [ 
 'myFunctionAlias' 
 ] 
  
 = 
  
 myFunction 
 ; 
  

If you are using Closure Library, exports can also be declared using the goog.exportSymbol and goog.exportProperty functions.

More information is available in the Closure Library documentation of these functions. However, be aware they have special compiler support and will be totally transformed in the compiled output.

Issues with Exports

Exports are different from externs in that they only create an exposed alias for consumers to reference. Within the compiled code, the exported symbol will still be renamed. For this reason, exported symbols must be constant, since reassigning them in your code would cause the exposed alias to point to the wrong thing.

This subtlety in renaming is especially complicated with respect to exported instance properties.

In theory, exports can allow smaller code-size compared to externs, since long names can still be changed to shorter ones within your code. In practice, these improvements are often very minor, and don't justify the confusion exports create.

Exports also don't provide an API for consumers to follow in the way externs do. Compared to exports, externs document the symbols you intend to expose, their types, and give you a place to add usage information. Additionally, if your consumers are also using Closure Compiler, they will need externs to compile against.

Create a Mobile Website
View Site in Mobile | Classic
Share by: