What is a namespace?

number

Object

/** * An object-literal namespace * @const */ var ns = {}; /** * A new class Foo as a namespace property * @constructor */ ns.Foo = function() {}; /** * This is also OK. The new class is not defined as a property. * @constructor */ function Foo() {}

@const

Do not define new types on typedefs

// Externs /** @typedef {{prop1: number, prop2: string}} */ var MyType; /** * Wrong! * @constructor */ MyType.Foo = function() {}; // Source /** @param {MyType} x */ function f(x) { var /** null */ n; n = new MyType.Foo; // Typechecked n = new x.Foo; // Typed as unknown }

MyType.Foo

x

MyType

MyType

What about other properties on typedefs?

// Externs /** @typedef {{prop1: number, prop2: string}} */ var MyType; /** * Wrong! * @type {number} */ MyType.prop3; /** * Wrong! * @type {number} */ MyType.prototype.prop4; // Source /** @param {MyType} x */ function f(x) { var /** null */ n; n = x.prop3; // Typed as unknown n = x.prop4; // Typed as unknown }

/** @record */ function MyType() {} /** @type {number} */ MyType.prototype.prop1; /** @type {string} */ MyType.prototype.prop2; /** @type {number} */ MyType.prototype.prop3; /** @type {number} */ MyType.prototype.prop4;

// Externs /** @typedef {function(number):number} */ var MyType; /** * Wrong! * @type {number} */ MyType.prop3; /** * Wrong! * @type {number} */ MyType.prototype.prop4; // Source /** @param {MyType} x */ function f(x) { var /** null */ n; n = x.prop3; // Typed as unknown n = x.prop4; // Typed as unknown }

MyType

MyType

// Externs /** @typedef {function(number):number} */ var MyType; /** @type {MyType} */ var MyType_; /** @type {number} */ MyType_.prop3; /** @type {number} */ MyType_.prop4;

MyType





Posted by Dimitris Vardoulakis, Software Engineer on the Closure Compiler team

When writing typedefs, it is easy to make mistakes and write definitions that are not supported by Closure Compiler. The source of the issue is that type checking for typedefs is unfortunately quite loose, especially for typedefs defined in externs files. As a result, the compiler does not warn about some malformed definitions. Then, the user thinks their code is getting typechecked when it is not. (Checking is stricter with the new type checker . You may want to consider it for your project.)The goal of this post is to show the common pitfalls, and suggest correct ways to define typedefs. This will alleviate the problem until the typedef-related checks are enforced by the compiler.In addition to the built-in types such as, etc, a programmer can define and use their own types: classes, interfaces, records, and typedefs. If a user-defined type is defined as a property of an object, this object must be a namespace. For example:Object literals defined withare namespaces. So are classes and functions. Typedefs arenamespaces.Since a typedef is not a namespace, you should not define new types on a typedef. The compiler may not warn, but it will not check such types correctly.You can see thatis not properly checked. It is checked when we refer to it directly, but not on a formal parametertyped. You can try this example using the debugger. Note that if we had definedin the source instead of the externs, the compiler would have warned. We saw that defining new types on typedefs is not supported. More generally, you should not define any extra properties on typedefs, even if these properties are not creating new types. The compiler may not warn, but it will not check these properties correctly.A common mistake people make is to define a typedef of a record type, and then define extra properties on it. The compiler will silently not typecheck in this case.The correct way to do this is with @record.A related mistake is to define a typedef of a function, and then define extra properties. Again, some typechecking will silently not happen.Closure Compiler does not fully support functions with properties. They are representable in the type system, but there is no notation to write such types.There is a somewhat hacky way to express functions with properties. Even though it is not entirely correct, it provides some type checking. We first define a typedefof the function type. Then, we define a variable or property of type, and then we define the extra properties on the variable.Defining functions with properties this way has two caveats. First, even though these properties should only be defined on, they are defined on more functions, so the compiler misses some inexistent-property warnings. Second, the old and the new type checker behave differently on such types. In this example, the new type checker does not warn but the old one does. Despite these caveats, this is an acceptable workaround to declare types of functions with properties.