I'm currently in the process of comparing the Google Closure Compiler and the Flow static type checker, in terms of expressiveness. What I like about the latter is that it apparently can represent tagged unions quite nicely. The manual gives this example:
type Result = Done | Error; // a disjoint union type with two cases
type Done = { status: 'done', answer: Matrix };
type Error = { status: 'error', message: string };
Is there a way to do something like this using Closure Compiler? That means some way to force certain proprties of an object to not only be of a specific type, but have a fixed value? And to use that value for type inference, to distinguish between different options in a union? I found no documentation to this effect.
There isn't an exact translation. Here's how I would write this:
/** @enum {string} */
var ResultStatus = {
DONE: 'done',
ERROR: 'error'
};
/** @record */
var Done = function() {};
/** @type {ResultStatus} */
Done.prototype.status;
/** @type {Matrix} */
Done.prototype.answer;
/** @record */
var Error = function() {};
/** @type {ResultStatus} */
Error.prototype.status;
/** @type {string} */
Error.prototype.message;
/** @type {Result|Error} */
var Result;
This is a very verbose syntax but provides the best type checking. Any object with those properties is assumed to match the type. Extra properties are allowed.
There's a much shorter syntax with slightly different behavior: typedefs.
/** @enum {string} */
var ResultStatus = {
DONE: 'done',
ERROR: 'error'
};
/**
* @typedef{{
* status: ResultStatus
* answer: Matrix
* }}
*/
var Done;
/**
* @typedef{{
* status: ResultStatus
* message: string
* }}
*/
var Error;
/** @type {Result|Error} */
var Result;
There are several other ways to write these types as well. Which you choose depends on what you want checked. For instance, do you want a warning if you misspell (or try to add) a property? Do you want an exact match on both property names or types, or are extra properties allowed?