Search code examples
javascriptgoogle-closure-compiler

Closure Compiler insists a value can be null when it cannot be


I have a structure which is typed as follows:

 /** @type {!Array<{
   *  cmd: string,
   *  params: !Array<{name: string}>,
   *  do: function(!Object, !Array<!Roll20Object>, !Array<string>, boolean):string
   * }>} */

One of the values loaded into the structure includes the following field:

   do: ((p,t,r,a) => {
      /** @type {!Array<string|number|boolean>} */
      let results = _.map(t, x => getTokenAttr(x,p.property));

GetTokenAttr's return type is {null|string|number}. _.map is typed as follows in the externs for underscore.js:

/**
 * Object-style annotation
 * @param {Object|Array} obj
 * @param {Function} iterator
 * @param {Object=} opt_context
 * @return {!Array|_}
 */

Closure gives the following error compiling the structure:

pf2utils.js:1029: WARNING - [JSC_TYPE_MISMATCH] initializing variable
found   : (Array|_|null)
required: Array<(boolean|number|string)>
                let results = _.map(t, x => getTokenAttr(x,p.property));

It seems to be claiming that results has the possibility of being null, but it is not clear where this is coming from. The do declaration promises that the t parameter is not null (as it's !Array<..> and the extern for _.map promises it does not return null (because of @return {!Array|_}). But Closure is treating _.maps return as (Array|_|null} instead, unprompted.

How can I tell Closure that this is a valid assignment and should be null free?


Solution

  • As _ is declared as a class (with @constructor), it is nullable by default, the type _ is equivalent to !_|null. The return type for map should be, if it can not return null, would be !Array|!_ not !Array|_.

    You can verify the different in the Closure compiler "debugger" playground with this snippet, where the assignment to x is allowed but the assignment to y is rejected:

    /** @constructor */
    function _() {}
    
    /** @type {!Array|_} */
    let x = null;  // valid
    
    /** @type {!Array|!_} */
    let y = null;  // type error