eslint-plugin-unicorn icon indicating copy to clipboard operation
eslint-plugin-unicorn copied to clipboard

Rule proposal: `no-this-outside-of-class`

Open zanminkian opened this issue 1 year ago • 13 comments

Description

In modern JavaScript, this should not appear outside of an class.

Fail

function foo () {
  this.bar;
}
function Foo(name) {
  this.name = name;
}
class Foo {
  foo() {
    function bar() {
      this.baz;
    }
  }
}

Pass

class Foo {
  foo() {
    this.name;
  }
}

class Foo {
  foo() {
    const bar = () => {
      this.name;
    }
  }
}

Proposed rule name

no-this-outside-of-class

Additional Info

No response

zanminkian avatar Sep 16 '24 16:09 zanminkian

This rule should be easy to implement. It is similar to this rule #2525

Once this rule is accepted, I will work on it.

axetroy avatar Jan 16 '25 14:01 axetroy

@sindresorhus Is this rule acceptable?

axetroy avatar Jan 21 '25 14:01 axetroy

Accepted

sindresorhus avatar Jan 22 '25 02:01 sindresorhus

Should we exclude class modification pattern?

Foo.prototype.bar = function () {
	this.baz()
}

fisker avatar Jan 22 '25 03:01 fisker

Should we exclude class modification pattern?

I don't think modifying prototype manually in morden JavaScript is a good practice.

zanminkian avatar Jan 22 '25 09:01 zanminkian

I don't think modifying prototype manually in morden JavaScript is a good practice.

What's the alternative? Subclass shouldn't be the same thing.

fisker avatar Jan 22 '25 10:01 fisker

Also, should we allow this in object methods?

fisker avatar Jan 22 '25 10:01 fisker

What's the alternative? Subclass shouldn't be the same thing.

I have no alternatives. Modifying an existing constructor's prototype seems unnecessary, except polyfills or intercepting/overriding something. Intercepting/Overriding something by modifying prototype is a little bit hacky. It breaks the open–closed principle.

Also, should we allow this in object methods?

I don't use it but I feel ok to allow it.

zanminkian avatar Jan 22 '25 11:01 zanminkian

Should we allow this in the top-level scope?

Many umd modules and some special environments may not have window and global, and this is needed for detection

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  } else {
    root.MyModule = factory();
  }
}(typeof self !== 'undefined' ? self : this, function () {
  const MyModule = {
    greet: function (name) {
      return `Hello, ${name}!`;
    },
    add: function (a, b) {
      return a + b;
    }
  };

  return MyModule;
}));

axetroy avatar Feb 14 '25 13:02 axetroy

Also, should we allow this in object methods?

It should be allowed, Vue2 options is this mode, otherwise there are many false positives

<script>
export default {
  methods: {
    foo () {
      this.name = 'xxx'
    }
  }
}
</script>

axetroy avatar Feb 14 '25 16:02 axetroy

Examples of false positives

new SDK({
    appKey: 'xxx',
    appId: 'xxx',
    onReady: function () {
        this.xxxxx
    }
})


<script>
export default {
  methods: {
    refresh: debounce(async function () {
       this.list = await getList()
    }, 500)
  }
}
</script>

axetroy avatar Feb 15 '25 09:02 axetroy

Should we allow this in the top-level scope?

Personally no. Your umd example is not modern JS. 99% projects do not need this. People, who really need this, should turn off this rule.

It should be allowed, Vue2 options is this mode, otherwise there are many false positives

Vue2 is EOL.

zanminkian avatar Mar 02 '25 15:03 zanminkian

Vue2 is EOL, But Vue's options mode is not.

And similar situations

export default defineComponent({
  methods: {
    mounted() {
        this.xxxxxx // 
    }
  }
})

Maybe we should wait for more feedback.

axetroy avatar Mar 03 '25 06:03 axetroy