Coder Social home page Coder Social logo

Comments (18)

ericelliott avatar ericelliott commented on August 16, 2024

The documentation isn't worded correctly, but I believe the rule behaves as expected.

I intend to write a blog post that describes the issue more accurately, soon.

Thanks for pushing back RE: docs. We do need to clean this up.

👍

from eslint-no-inferred-method-name.

johnstonbl01 avatar johnstonbl01 commented on August 16, 2024

Hey Kyle. Before I address the issue, I just want to say how bizarre and humbling it is to have two of my JS heroes comment on one of my repos. I began learning JavaScript only 6 months ago, so I'm sure you can imagine how crazy this feels!

Back to the issue at hand - I will definitely review this tomorrow and the issue you linked in Babel to update the examples and introduction to be accurate. Thanks so much for taking the time to comment, and I apologize for the poorly worded documentation.

from eslint-no-inferred-method-name.

ericelliott avatar ericelliott commented on August 16, 2024

@getify It's easy to make this mistake, since typical function expressions can use the specified function name inside the function without a lexical binding. For example:

const bar = function foo () { console.log(typeof foo); };
bar(); // logs: 'function'

foo(); // no lexical 'foo' exists:
// ReferenceError: foo is not defined

@johnstonbl01 I've written up a gist that more accurately describes the issue using the repeat() example:

https://gist.github.com/ericelliott/a79a54e729f957b5debe

from eslint-no-inferred-method-name.

getify avatar getify commented on August 16, 2024

👍

For the record:

  1. I have not tried the rule myself, so I was only commenting on the documentation itself. If the code is correct and the documentation is wrong, my apologies for being too alarmist.
  2. I am glad such a rule will exist, as it would seem to be helpful.
  3. Apologies for my initially too-harsh tone. Overreaction on my part.
  4. It should be a warning at most (and not an error), because this is valid if I wanted to do it, though you may very well want the linter to wag its finger at you for doing so:
function foo(x) {
   return obj.foo(x);
}

var obj = {
   foo(x) {
     if (x < 10) return foo(x * 2); // `foo` here comes from the function, not the concise method
     return x;
   }
};

obj.foo(4);  // 16

from eslint-no-inferred-method-name.

johnstonbl01 avatar johnstonbl01 commented on August 16, 2024

Hey guys. Below is the change I'm proposing to the docs. I spent some time this morning re-reading your statements here, and reviewing the Babel issue again. I think I'm on the same page now, and I apologize for not completely getting it. Indeed as Kyle mentioned, I thought the original issue was with the name inferrence and not necesarily the lexical name binding. That was my mistake. Luckily, on some level I did understand the issue with the code and I believe that the rule logic is sound. @ericelliott - let me know when you've completed that blog post, and I'll link to it as well in the overview.

Overview

In ES6, compact methods and unnamed function expression assignments within object literals do not create a lexical identification (name) binding that corresponds to the function name identifier for recursion or event binding. The compact method syntax will not be an appropriate option for these types of solutions, and a named function expression should be used instead. This custom ESLint rule will identify instances where a function name is being called and a lexical identifier is unavailable within a compact object literal.

More information on this can be found:

Note - Tests are provided in the repo, but not necessary for installation or use of the rule.

Example Changes

Example 1

In the example below, 1 error is generated because foo is being called recursively when there is no lexical name binding for the foo function using the concise object literal syntax. See links above for in-depth discussion on this behavior.

const bar = {
  name: 'Bar',
  types: [
    { f: 'function' },
    { n: 'number' }
  ],
  foo (f, n) {   // this function will not have any lexical binding for recursive calls
    if (typeof f === 'function') {
      f();
    } else {
      throw new Error('foo: A Function is required.');
    }

    n -= 1;
    if (!n) {
      return undefined;
    }

    return foo(f, n);   // error on this line
  }
};

bar.foo(() => {console.log('baz');}, 3);
//baz
//ReferenceError: foo is not defined

Example 2

In this example, no errors are generated because the function expression explicitly defines a lexical identifier.

const bar = {
  name: 'Bar',
  types: [
    { f: 'function' },
    { n: 'number' }
  ],
  foo: function foo (f, n) {   // this function explicitly defines a lexical name for the method
    if (typeof f === 'function') {
      f();
    } else {
      throw new Error('foo: A Function is required.');
    }

    n -= 1;
    if (!n) {
      return undefined;
    }

    return foo(f, n);
  }
};

bar.foo(() => {console.log('baz');}, 3);
//baz
//baz
//baz

Updated Error Message

I propose changing the current error message:

{func} has no defined method name. Use syntax - foo: function foo {...}.

to

{func} has no lexical name binding. Use syntax - foo: function foo {...}.

If you think it should be something more descriptive, please let me know.

Error vs Warning

I'm happy to change the docs to recommend setting the value of the rule to 1 (for warning) rather than 2 (for error). However, the no-undef rule that's in ESLint by default will continue to flag these instances as an error. For consistency, I assumed that this rule should also show an error. If that assumption is incorrect and we think that it's better as a warning, I'm happy to change it.

Lastly - thanks to both of you for the help and guidance. It's really been a great learning experience.

@getify - No worries on the original tone. I knew where you were coming from. 👍

from eslint-no-inferred-method-name.

getify avatar getify commented on August 16, 2024

Looks great to me!

I'm happy to change the docs to recommend setting the value of the rule to 1 (for warning) rather than 2 (for error)

I don't know if this is possible, but I'd suggest:

  • It's an error if there's no lexical identifier foo in any of the lexical scopes. Can you definitively tell that the lexical identifier is completely missing?

    Example:

    var o = { foo(){ foo() } };   // should be an error!
  • Otherwise, it's a warning if you use a lexical name that matches the concise method identifier, but the same lexical name already exists in the scope. Like in my previous example, you might want to do it... but I bet more times than not, it's a mistake. That's why I think it should be a warning instead of an error, in that specific case.

    Example:

    function foo() {}
    var o = { foo(){ foo() } };  // should be a warning!

from eslint-no-inferred-method-name.

RReverser avatar RReverser commented on August 16, 2024

{func} has no lexical name binding. Use syntax - foo: function foo {...}.

One more proposal - why suggesting to completely change the way method is defined instead of showing warning/error on call itself and suggesting to change return foo(f, n) to return this.foo(f, n)? This makes more sense, since you're also passing context which in most cases is important for object methods, and with named function expression you're losing it.

from eslint-no-inferred-method-name.

ericelliott avatar ericelliott commented on August 16, 2024

@RReverser return this.foo() won't work if this changes, as is the case with .call() or .apply().

from eslint-no-inferred-method-name.

RReverser avatar RReverser commented on August 16, 2024

@ericelliott It's much less likely to be mistake than when someone doesn't pass this at all by calling object method like regular named function. this is important, and ability to substitute different context by special APIs is not really an argument not to pass it at all.

from eslint-no-inferred-method-name.

ericelliott avatar ericelliott commented on August 16, 2024

@RReverser The argument here is which form is less likely to lead to code that doesn't work as expected. this is more error-prone than the alternative in this case.

from eslint-no-inferred-method-name.

RReverser avatar RReverser commented on August 16, 2024

@ericelliott Do you suggest to also provide ESLint rule that restricts using this just because it can be error-prone?

from eslint-no-inferred-method-name.

johnstonbl01 avatar johnstonbl01 commented on August 16, 2024

Guys - let's keep it civil and on-topic, please.

@RReverser Please keep in mind that this is a custom rule, and there's no plan for this to be integrated into the standard ESLint rules. If you don't agree with the way it is written, then it's possible to create your own to use or not use this one at all. That's the great thing about ESLint, in my opinion.

@getify I think that should be possible, but I need to dig in and have a look. I'll push the README changes later today and get back to you on the warning / error functionality.

from eslint-no-inferred-method-name.

RReverser avatar RReverser commented on August 16, 2024

@johnstonbl01 Of course I do understand that - I'm just pointing to other possible mistakes that this linting rule might accidentally push other devs towards (like not preserving context in object method when calling it recursively). Sorry if polluting the thread, I made my point so will stop here :)

from eslint-no-inferred-method-name.

johnstonbl01 avatar johnstonbl01 commented on August 16, 2024

Thanks, @RReverser . :)

from eslint-no-inferred-method-name.

getify avatar getify commented on August 16, 2024

Regarding the this in the warning/error message... i'd say that only makes sense if this shows up anywhere in the function already, such as in reference to some other property/method. If so, then clearly it's a this aware method and the call in question should very likely be this.foo().

But if there's no other this reference in the function, inferring that it's a this-aware method (instead of just a function on an object namespace, as is the case with practically all modules) is too far a jump IMO. That sort of assumption is exactly the kind of thing that makes me disable such rules entirely in my linters.

from eslint-no-inferred-method-name.

ericelliott avatar ericelliott commented on August 16, 2024

I don't think the rule needs any logic changes.

We can adequately handle either case by changing the rule text:

{func} has no name binding. Use `foo: function foo {...}` or call with `this.foo`

For the sake of simplicity, let's assume the reader knows enough JS to figure out the rest.

from eslint-no-inferred-method-name.

RReverser avatar RReverser commented on August 16, 2024

@ericelliott Sounds good to me!

from eslint-no-inferred-method-name.

johnstonbl01 avatar johnstonbl01 commented on August 16, 2024

Ok, guys. Here are the updates:

  • Documentation overview updated to provide accurate description of the issue
  • Documentation examples replaced with those mentioned above
  • Error message updated to {func} has no lexical name binding. Use syntax 'foo: function foo {...}' or call with 'this.foo()'
  • Tests updated to reflect new error message
  • Updated a function name in rule code to be more descriptive
  • Confirmed all tests pass after changes

Closing this issue for now - thanks for the input, everyone!

from eslint-no-inferred-method-name.

Related Issues (5)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.