I have been confused for a long time regarding the following code snippet:
/**
* Pair of width and height.
* @param {string} width
* @param {string} height
* @constructor
* @struct
*/
var Pair = function(width, height) {
/** @type {string} */
this.key = key;
/** @type {string} */
this.value = value;
};
vs
/**
* @typedef {{width: string, height: string}}
*/
var Pair;
Basically I need to create a new type and highly confused about which one to use when?
which one to use when?
This is to some extent a matter of personal preference and coding style. Closure Compiler leans more towards the pseudoclassical inheritance pattern found in Java with classes, interfaces, methods, etc. which is different to other approaches. See Inheritance Patterns in JavaScript by Michael Bolin.
See also Annotating JavaScript for the Closure Compiler which has the most concise definitions of the various tags.
For myself, I use the pseudoclassical inheritance pattern. Therefore 99% of the time I am defining classes with @constructor
which are meant to be used with the new
keyword. These classes are also marked with @struct
so they have a fixed set of properties and methods. Around 80% of my classes state that they @implement
an @interface
because that allows me to use the interface types and not be tied to a particular class implementation.
Occasionally I will use @typedef
to define an object that has some minimal set of properties. For me, this type of object usually has no functions defined on it, it is merely a way to aggregate some properties.
Here's an example from my code:
/** A regular expression and the replacement string expression to be used in a
* `String.replace()` command.
* @typedef {{regex: !RegExp, replace: string}}
* @private
*/
Terminal.regexPair;
I can use that typedef elsewhere:
/** Set of regular expressions to apply to each command.
* @type {!Array.<!Terminal.regexPair>}
* @private
*/
this.regexs_ = [];
The advantage is that it is easy to make something of this type:
/** Add regular expression and replacement string to be used by `String.replace()`
* for transforming script commands before they are executed.
* @param {!RegExp} regex regular expression for finding a name to transform
* @param {string} replace the replacement string for use with `String.replace()`
*/
Terminal.prototype.addRegex = function(regex, replace) {
this.regexs_.push({regex:regex, replace:replace});
};
Notice that I made an anonymous object with {regex:regex, replace:replace}
instead of using the new
operator. Yet the compiler is checking that this object has the required (minimal) set of properties defined by Terminal.regexPair
.
But there are many other philosophies about how to write JavaScript code. And you could certainly define a typedef that requires functions with specified signatures.