Skip to content

Structural Interfaces in Closure Compiler

Michael Zhou edited this page Mar 11, 2016 · 13 revisions

As of the v20150729 Closure Compiler release, we've been working toward having structural interfaces and covariant record types. Here's an overview of what changes that means for users of the Closure Compiler type system.

Structural Interfaces

Currently, Closure Compiler has nominal interfaces. For example, for:

/** @interface */
function PNumI() {};
/** @type {number} */
PNumI.prototype.p;

/** @constructor */
function Foo() {};
/** @type {number} */
Foo.prototype.p = 5;

since Foo doesn't explicitly @implement PNumI, it isn't considered a subtype, even though it has all of the required properties for implementations of PNumI.

WARNING - initializing variable
found   : Foo
required: PNumI
var /** !PNumI */ x = new Foo;
                     ^

With structural interfaces (declared with the @record annotation), Foo would be considered to match. Since structural interfaces are very similar to the existing record types, records also match:

/** @record */
function PNum() {};
/** @type {number} */
PNum.prototype.p;

var /** !PNum */ x = new Foo;  // OK with structural interfaces
var /** !PNum */ y = { p: 5 }; // OK with structural interfaces

For now, we use the @record annotation to denote structural interfaces, but we hope to convert all interfaces to structural since it's simpler to have only one style of interfaces.

Records vs. Structural Interfaces

Since record types are already matched structurally, they are similar to structural interfaces. Note that unlike with traditional records, though, structural interfaces have names, and can be used with @implements, @extends, etc.

Also, since structural types have a name, they can be used recursively, as in:

/** @record */
function Recursive(){};
/** @type {?Recursive} */
Recursive.prototype.next;

Covariant Records

In order to align records better with structural interfaces, we have also converted record types from being invariant to being covariant. Thus, programs like the following are now allowed by Closure Compiler:

function f(/** { p : number } */ x) {
  var /** { p : (number|string) } */ y = x;
}
Clone this wiki locally