Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed bug - Certain static member names cannot be used due to non overridable member names on the constructor function type. #5396

Closed
wants to merge 2 commits into from

Conversation

cjbarre
Copy link

@cjbarre cjbarre commented Oct 25, 2015

Proposed patch for issue #442.

Introducing a set of contextually reserved identifier names seems like a big deal, so I'll happily go back and set that up in whatever way seems best, if anyone has suggestions. I'm new at this so that's what I came up with. I think I added the test correctly and the rest of the baselines look good. I had to modify one test because it was using 'name' as a static identifier name. I don't believe the test was specifically testing that bug, but possibly one that was actually being caused by this issue, I changed the property name from 'name' arbitrarily to 'beep' and I don't believe it will affect the test.

I look forward to a review of this and suggestions from everyone. I've never done a PR so if I did this incorrectly please let me know 馃憤

@msftclas
Copy link

Hi @cjbarre, I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution!

In order for us to evaluate and accept your PR, we ask that you sign a contribution license agreement. It's all electronic and will take just minutes. I promise there's no faxing. https://cla.microsoft.com.

TTYL, MSBOT;

@msftclas
Copy link

@cjbarre, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR.

Thanks, MSBOT;

@cjbarre cjbarre changed the title Fixed bug Fixed bug - Certain static member name cannot be used due to non overridable member names on the constructor function type. Oct 25, 2015
@cjbarre cjbarre changed the title Fixed bug - Certain static member name cannot be used due to non overridable member names on the constructor function type. Fixed bug - Certain static member names cannot be used due to non overridable member names on the constructor function type. Oct 25, 2015
@DanielRosenwasser
Copy link
Member

Hey @cjbarre, thanks for the PR!

Right now your checks are in the grammar checking code, whereas I would suggest you should perform the check in checkVariableLikeDeclaration.

I feel like a better approach might be to check whether or not the members exist within the global Function interface.

That might prompt a question as to whether or not we should enforce the same restrictions on an interface with a call/construct signature. My feeling is no, but maybe others could weigh in. @ahejlsberg @RyanCavanaugh @JsonFreeman

@@ -15082,6 +15089,9 @@ namespace ts {
break;

case SyntaxKind.StaticKeyword:
if (node.kind === SyntaxKind.Identifier && reservedStaticPropertyNames[node.symbol.name]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes static declarations with name "toString" for instance illegal, if you are using a map, use ts.hasProperty to check if the property exist on the object it self and not its prototype chain.

@DanielRosenwasser
Copy link
Member

@mhegazy what do you think of checking the Function interface?

@@ -1724,6 +1724,10 @@
"category": "Error",
"code": 2657
},
"'{0}' cannot be used as a static member identifier.": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a lit bit more information here; preferably something that will point that classes constructors are inherently functions. something like:

Duplicate identifier 'name'. Class constructor functions implicitly have a property with the same name

@mhegazy
Copy link
Contributor

mhegazy commented Oct 26, 2015

@mhegazy what do you think of checking the Function interface?

i agree this is a cleaner approach.

@JsonFreeman
Copy link
Contributor

Agreed about the Function interface. But to clarify something, overriding properties inherited from Object.prototype works, but from Function.prototype does not work? Is that the general rule?

@DanielRosenwasser for your question, I think interfaces with c/c signatures should not have this check. The reason is because interfaces are merely descriptive. My understanding of this check is that it is erroring on code that will try to override these properties, which is not what an interface is doing.

@cjbarre
Copy link
Author

cjbarre commented Oct 26, 2015

Right now your checks are in the grammar checking code, whereas I would suggest you should perform the check in checkVariableLikeDeclaration.

@DanielRosenwasser I agree that sounds like a much better place for something like this.

I feel like a better approach might be to check whether or not the members exist within the global Function interface.

This will ensure that every member on the global Function interface will be checked and centralize the location of these reserved identifier names. It also makes the intention of the code more clear. Sounds good to me!

I think we need a lit bit more information here; preferably something that will point that classes constructors are inherently functions. something like:

Duplicate identifier 'name'. Class constructor functions implicitly have a property with the same name

Absolutely @mhegazy, would you mind explaining this

this makes static declarations with name "toString" for instance illegal, if you are using a map, use ts.hasProperty to check if the property exist on the object it self and not its prototype chain.

a little further?

@DanielRosenwasser for your question, I think interfaces with c/c signatures should not have this check. The reason is because interfaces are merely descriptive. My understanding of this check is that it is erroring on code that will try to override these properties, which is not what an interface is doing.

@JsonFreeman Doesn't defining an interface imply that most of the time it will be implemented at some point? Could it be confusing if someone tries to do this:

interface A {
  static name; // No error
}

class B implements A {
   static name; // Error
}

I think I understand your position, that TypeScript interfaces can be useful without implementations and aren't the same as typical language interfaces like in C# where an interface will always have some sort of implementation, right?

@DanielRosenwasser @mhegazy @JsonFreeman Thanks a lot for the review guys I appreciate it! I have a lot to learn about the compiler :P

@DanielRosenwasser
Copy link
Member

@mhegazy meant that if you index into the object with the symbol name like you're currently doing, it will perform a lookup in the object's prototype as well. Since that object's prototype is Object, there will be existing properties like toString, so if your symbol's name is "toString", you'll accidentally say that the lookup succeeded.

We have a function called hasProperty which takes care of this issue, so you could use that.

If you end up using the Function type, I think we have other helper functions. Something like getPropertyOfType.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 26, 2015

@cjbarre in your change you have a check like:

  if (node.kind === SyntaxKind.Identifier && reservedStaticPropertyNames[node.symbol.name])

checking if the object reservedStaticPropertyNames has a property with value node.symbol.name is not correct, as it will check properties on the object it self (e.g. length, name, arguments, caller) as well as on its prototype, Object in this case. Object has a set of predefined members like toString and valueOf, so now your look up will return true if your symbol is toString, what you should use instead is hasOwnProperty to make sure you are not looking up prototype properties.

Here is a sample i ran in my browser F12 console.

var map = {"a" : "a"};
> undefined
!!map["a"];
> true
!!map["toString"];
> true
map.hasOwnProperty("a");
> true
map.hasOwnProperty("toString");
> false

@cjbarre
Copy link
Author

cjbarre commented Oct 26, 2015

@mhegazy @DanielRosenwasser Thanks guys I understand now! I'm going to use the Function type, so I'll be sure to check out those helper functions.

Any opinions on @JsonFreeman's comments on whether or not interfaces with c/c signatures should have to follow the rules?

@mhegazy
Copy link
Contributor

mhegazy commented Oct 26, 2015

I agree with @JsonFreeman. we should only do this checking for static properties on classes (and merged namespaces* as well), but not on anything with a call/construct signature.

  • here is a test:
class C {
}

namespace C {
     export var name: string;  // should be a duplicate property declaration as C already have a "name" property from the Function interface.
}

@JsonFreeman
Copy link
Contributor

@mhegazy Good point about the merged declarations. What about when the class or namespace is ambient? Again, ambients are just descriptive. So should these properties be allowed for ambients?

Here are some interesting examples:

declare class C {
    static length(): number;
}
class C { }
declare namespace C {
    export function length(): number;
}
declare class C { }
declare namespace C {
    export function length(): number;
}
declare class C { }
namespace C {
    export function length(): number { return 0 }
}

@mhegazy
Copy link
Contributor

mhegazy commented Oct 26, 2015

I think you are right. ambient should not be errors. we do not know how it was implemented.

@JsonFreeman
Copy link
Contributor

@mhegazy but what about the very last example?

@mhegazy
Copy link
Contributor

mhegazy commented Oct 26, 2015

i would say error. you are trying to add a function with the same name to an existing class, which possibly will break some functionality, so i would assume you want to know about it.

@JsonFreeman
Copy link
Contributor

Yes, so the rule would be that if the property declaration itself is non-ambient, it is an error.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 26, 2015

correct

@mhegazy
Copy link
Contributor

mhegazy commented Oct 27, 2015

@cjbarre please let us know if you have any questions.

@cjbarre
Copy link
Author

cjbarre commented Oct 27, 2015

@mhegazy I definitely will, having a busy week. I think the situation has been complicated a little more than the original request. I'm not yet familiar with the concept checking merged namespaces and checking for c/c signatures.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 28, 2015

Feel free to ping @DanielRosenwasser, myself, or @JsonFreeman if you have any questions. here is some documentation about declaration merging in general https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Declaration%20Merging.md

@cjbarre
Copy link
Author

cjbarre commented Oct 28, 2015

@mhegazy @JsonFreeman @DanielRosenwasser

Opinions on a different diagnostic message for namespace merge conflicts? I think it's less obvious than when trying to declare a conflicted static property on a class.

Cannot export an identifier named 'name' in a namespace declaration that is being merged with a class declaration. Class constructor functions implicitly have a property with the same name.

Something to that effect?

@mhegazy
Copy link
Contributor

mhegazy commented Oct 28, 2015

"Duplicate property declaration" error is fine along with some explanation of "Class constructors implicitly has a property 'name`"

@JsonFreeman
Copy link
Contributor

I am not a fan of the word "implicit" being used in an error message because I think it confuses most users (except implicit any errors). I would say something like "Static property 'name' inherited from type Function cannot be overriden". @cjbarre while I think it is nice to distinguish the merged declaration case, I can't think of a good wording that won't sound confusing.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 7, 2016

@cjbarre are you still perusing this PR? if so please refresh it and resolve merge conflicts.

@mhegazy
Copy link
Contributor

mhegazy commented Dec 30, 2016

Closing in favor of #12065

@mhegazy mhegazy closed this Dec 30, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants