google / closure-compiler

A JavaScript checker and optimizer.
https://developers.google.com/closure/compiler/
Apache License 2.0
7.31k stars 1.15k forks source link

JSC_INEXISTENT_PROPERTY if lends is used with object reference #747

Open sahilb opened 9 years ago

sahilb commented 9 years ago

If we use '@lends' with object definition it works but if we re-use this object reference at some other place in the code for 'lends' it does not work.

This will throw the warning: JSC_INEXISTENT_PROPERTY: Property getJerseyNumber never defined on player

var getDetails = { getJerseyNumber: function() { return Math.random(); } };

function mixIn(source, delta) { for (var i in delta) { source[i] = delta[i]; } } var player = { name: 'Tom', sport: 'Football' };

alert(player.sport);

mixIn(player, /* @lends {player} / getDetails);

alert(player.getJerseyNumber());

/////

But this works. mixIn(player, /* @lends {player} / { getJerseyNumber: function(){} });

dimvar commented 9 years ago

The first one doesn't work because lends can only appear on object literals. See: https://developers.google.com/closure/compiler/docs/js-for-compiler#tag-lends

If the compiler didn't warn you about the misplaced lends, it's a bug.

sahilb commented 9 years ago

Can I request this as a feature, where @lends could recognize the definition of getDetails here ? That would solve a lot of our issues with mixIn code.

dimvar commented 9 years ago

We don't plan to work on that. (It would complicate the order of processing statements during type checking.)

sahilb commented 9 years ago

So in that case, what would be the solution to fix the warning in the above case. We can't use object literal here.

dimvar commented 9 years ago

Right, we should warn about this.

sahilb commented 9 years ago

I meant to ask if there is a way, to not get a warning if object literal is not used ? Some way to tell the compiler that the properties on the player will get properties added from another object ? Maybe something like :

/* player inherits from getDetails / mixIn(player, getDetails);

Thanks.

pauldraper commented 9 years ago

Some way to tell the compiler that the properties on the player will get properties added from another object?

That's exactly what a prototype ("class") does.

/**
 * @constructor
 * @param {string} name
 * @param {string} sport
 */
var Player = function(name, sport) {
    this.name = name;
    this.sport = sport;
};

Player.prototype.getJerseyNumber = function() {
    return Math.random();
};

var player = new Player('Tom Brady', 'Football');

alert(player.getJerseyNumber());

For cases in which you want to have multiple "mixins", it is not really possible to have the compiler recognize this; see this discussion, or this discussion.

sahilb commented 9 years ago

Thanks @pauldraper .

The code sample provided is a good demonstration to fix the above issue, but basically i was looking for a way to have the compiler recognize multiple mixins. This is a handy construct for JS programming and is being used quite a lot (one popular code base using this is jquery).

Looking forward to see this feature in future.

ChadKillingsworth commented 9 years ago

For jQuery, we have used a different technique. See https://github.com/google/closure-compiler/wiki/jQuery-Expansions

Mixins in general don't provide any new functionality and I don't know of anyone actively working on them.