google / google/closure-compiler

Accessing a possibly-undefined property is not caught for a union of @struct types

Open
#1,141 1 comment 0 reactions 0 assignees View on GitHub
Types
Dominant language
JavaScript
Stars
7.7k
Forks
1.2k
Avg merge
2d 12h
Merged PRs (30d)
6

Description

Consider the following code:

``` javascript
// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @output_file_name default.js
// ==/ClosureCompiler==

/** @struct @constructor */
var A = function A() {
/** @type {!number} */
this.a = 1;
/** @type {!number} */
this.c = 100;
};

/** @struct @constructor */
var B = function B() {
/** @type {!number} */
this.b = 2;
/** @type {!number} */
this.c = 200;
};

/** @param {!(A|B)} param */
function test(param) {
alert('Here is A.a:' + param.a);
alert('Here is (A|B).c:' + param.c);
}

test(new B());
```

It compiles successfully but won't work in release. Looks like `{!(A|B)}` makes compiler think that **any** of the fields from `A` and `B` is fine. Though, the only safe assumption here is a field that exists in **both** `A` and `B`. When code needs to refer a field specific to `A` or `B` an explicit type cast should be required to figure out the intent. As it is it's error prone since with `@struct` one expects to have an error when accessing an undefined property.

I'd expect compiler to require something like this:

``` javascript
/** @param {!(A|B)} param */
function test(param) {
alert('Here is A.a:' + param.a); // <== ERROR!
var paramA = /** @type {!A} */ (param);
alert('Here is A.a:' + paramA.a); // <== OK
alert('Here is (A|B).c:' + param.c);
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.