-
Notifications
You must be signed in to change notification settings - Fork 1.2k
A word about the type Object
@type {Object}
may seem like the right type for a lot of JavaScript patterns, but in the Closure Compiler type system, Object is a very loose type. There are usually better alternatives.
NOTE: this article pre-dates "strictCheckTypes". With "strictCheckTypes" enabled the compile will warn about referencing properties that only available on subclasses and the Object type is no longer loose.
Namespaces are best typed as @const
(or const
for simple names) without a type annotation:
const ns = {};
/** @const */ ns.anInnerNamespace = {};
This tells the Closure Compiler to track the object as a unique, anonymous type. By contrast, annotations like @const {Object}
and @type {Object}
tell the compiler to treat it as any Object in the program (including any subtype of Object).
Note: @const
indicates only that the name to which the value is assigned is never overwritten. In particular, for a namespace, it does not indicate that the set properties of the object fix nor that the properties values are themselves immutable.
Objects used to define a set of values should be typed with @enum
. Even if you don't use the enum name as a type anywhere, the enum definition restricts the object type and allows strong typing of the referenced values.
/** @enum {ValueType} */
var enum = {
KEY: value
};
NOTE: For many uses JavaScript's Map collection is a better choose. This section is obsolete since ES6 introduced a built-in Map
, which has nice properties that Object lacks.
Objects used as maps for runtime additions or lookups should restrict their key and value types using generics.
/** @type {!Object<number, number>} */
var map = {};
/** @const {!Object<string, ValueType>} */
var map = {
'string key': value
};
"Object" when used as a function parameter type when expecting a collection of properties, are often better expressed as a record type:
/** @param {{key:value}} props */
function f(props) {}
Where optional values should include "undefined":
/** @param {{required:string, optional:(string|undefined)}} props */
function f(props) {}
Record types tend to be verbose, and a typedef is often more practical:
/** @typedef {{required:string, optional:(string|undefined)}} */
var Params;
/** @param {Params} props */
function f(props) {}
Note that record types are non-nullable by default.
If you got here because you got a warning from the compiler about a weak namespace type and simply want to silence the warnings, you can use the type Object<?,?>
which will tell the compiler that you are deliberately using a weak type.