ntnyq/prefer-object-method-syntax
Prefer method syntax for inline functions in object literals.
- 🔧 The
--fixoption on the command line can automatically fix some of the problems reported by this rule. - 💡 Some problems reported by this rule are manually fixable by editor suggestions.
Why this rule?
The ESLint core object-shorthand rule already prefers method syntax for anonymous function expressions:
const object = {
method: function () {},
}However, its primary responsibility is broader: it also handles property shorthand, long-form syntax, and consistency across an object. Arrow functions are allowed by default. The avoidExplicitReturnArrows option only checks a subset of block-bodied arrow functions and does not report arrows that use lexical bindings such as this, arguments, super, or new.target.
The core rule also skips named function expressions and fixes anonymous ordinary function expressions even though converting one to a method removes its constructibility and changes its prototype behavior.
This rule focuses only on inline functions used as object property values. It:
- checks function expressions and, by default, arrow functions;
- reports expression-bodied and block-bodied arrows;
- still reports arrows that require lexical bindings, while withholding unsafe fixes;
- reports named function expressions without removing their inner binding;
- supports exact property-name exceptions;
- keeps automatic fixing opt-in and limited to the safe subset.
To avoid duplicate reports, configure the core rule to handle only property shorthand:
export default [
{
rules: {
'object-shorthand': ['error', 'properties'],
'ntnyq/prefer-object-method-syntax': 'error',
},
},
]📖 Rule Details
This rule reports function expressions and disallowed arrow functions used directly as object property values.
const dispose = () => {}
const object = {
dispose,
method() {},
async asyncMethod() {},
*iterator() {},
get value() {
return this.internalValue
},
set value(value) {
this.internalValue = value
},
}const object = {
method: function () {},
asyncMethod: async function () {},
iterator: function* () {},
dispose: () => {},
calculate: value => value * 2,
}Named function expressions are also reported, but are not automatically fixed because method syntax cannot preserve their inner name binding:
const object = {
recursive: function inner() {
return inner()
},
}The non-computed __proto__ form is ignored because it changes the created object's prototype rather than defining a normal property:
const object = {
__proto__: function () {},
}🔧 Options
export type Options = [
{
allowArrowFunctions?: boolean
allowedPropertyNames?: string[]
fix?: boolean
},
]Defaults:
allowArrowFunctions:falseallowedPropertyNames:[]fix:false
allowArrowFunctions
When true, arrow functions are allowed as object property values. Function expressions are still reported.
// options: [{ allowArrowFunctions: true }]
const object = {
allowed: () => {},
reported: function () {},
}allowedPropertyNames
Static property names in this list are ignored for both function expressions and arrow functions.
// options: [{ allowedPropertyNames: ['computed', 'get', 'set'] }]
const object = {
computed: () => value,
get: function () {},
['set']: value => update(value),
}Identifier, string, number, and statically computed property names are supported. A dynamic computed property cannot match this option:
const object = {
[methodName]: () => {},
}fix
When true, the rule automatically fixes only conversions that preserve the relevant function semantics.
The rule can automatically convert:
- anonymous async, generator, and async-generator function expressions;
- block-bodied arrow functions that do not depend on lexical bindings.
const before = {
load: async function () {},
iterate: function* () {},
dispose: () => {
cleanup()
},
}
const after = {
async load() {},
*iterate() {},
dispose() {
cleanup()
},
}The rule does not automatically fix:
- ordinary function expressions, because methods are not constructible and do not have the same
prototypeproperty; - named function expressions, because their inner name binding would be removed;
- expression-bodied arrows, because conversion requires introducing an explicit
return; - arrows that use lexical
this,arguments,super, ornew.target; - arrows containing direct
eval(...), which can access lexical bindings dynamically; - conversions that would remove or relocate comments.
For example, this arrow must retain the surrounding class instance as its this value:
class Controller {
#document = document
create() {
return {
dispose: () => {
this.#document.removeEventListener('click', handleClick)
},
}
}
}A semantics-preserving manual refactor keeps the arrow in the same lexical scope:
class Controller {
#document = document
create() {
const dispose = () => {
this.#document.removeEventListener('click', handleClick)
}
return {
dispose,
}
}
}Editor suggestions may be available for conversions that require manual review. In particular, the suggestion for an ordinary function expression warns that the resulting method will no longer be constructible.
🚀 Version
This rule was introduced in eslint-plugin-ntnyq v0.15.0