Search code examples
javascriptgoogle-closure-compiler

strange object property behavior with closure compiler ADVANCED_OPTIMIZATIONS


When running the following code compiled with ADVANCED_OPTIMIZATIONS, I get an unexpected output (for me at least).

/**
 * @typedef {{
 *      version: string,
 *      api_host: string
 * }}
 */
var AppProps;

/**
 * 
 * @param {AppProps} props 
 */
window['app'] = function(props) {
    props = props || {};
    console.log("props.api_host = ", props.api_host);
    console.log("props['api_host'] = ", props['api_host']);
};

Result:

props.api_host =  undefined 
props['api_host'] =  http://localhost:8080

Is this expected behavior for compiled object literals? Should I always be using string accessors for Object properties (something I almost never do)?

Any insight to this behavior will be greatly appreciated.

Cheers and thank you!

PS: closure-compiler version is v20191111, also the compiler has 0 knowledge of the object literal being passed in, with the exception of the @typedef. Function invocation happens outside of the compiled code.

PPS: Here is the rewritten code, which explains the output, but not the reasoning.

console.log("props.api_host \x3d ",a.B);
console.log("props['api_host'] \x3d ",a.api_host)

I would not expect closure-compiler to rewrite the first statement as it has, given the provided @typedef. What am I doing wrong here?


Solution

  • After going through the docs and carefully considering my setup, I have found the errors of my ways, and a satisfactory explanation for the results.

    Since everything within the compiled code, including my @typedef, is subject to rewriting, what I actually needed was an extern to describe the shape of this externally provided object.

    externs.js

    /**
     * @interface
     */
    function AppProps() {}
    
    /**
     * @type {string}
     */
    AppProps.prototype.version;
    
    /**
     * @type {string}
     */
    AppProps.prototype.api_host;
    

    app.js

    /**
     * 
     * @param {AppProps} props 
     */
    window['app'] = function(props) {
        props = props || {};
        console.log("props.api_host = ", props.api_host);
        console.log("props['api_host'] = ", props['api_host']);
    };
    

    The resulting output now works as expected.