Skip to content

Commit d032be5

Browse files
bmiddhaCopilot
authored andcommitted
feat(eslint): cover private methods
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ebd5bf2-c44b-42d5-be25-e7936d4b0a14 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 8962a20 commit d032be5

8 files changed

Lines changed: 69 additions & 32 deletions

File tree

common/changes/@rushstack/eslint-config/native-private-fields_2026-08-18-12-00-00.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"changes": [
33
{
44
"packageName": "@rushstack/eslint-config",
5-
"comment": "Enable the rule that prefers ECMAScript private class fields.",
5+
"comment": "Enable the rule that prefers ECMAScript private class members.",
66
"type": "minor"
77
}
88
]

common/changes/@rushstack/eslint-plugin/native-private-fields_2026-08-18-12-00-00.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"changes": [
33
{
44
"packageName": "@rushstack/eslint-plugin",
5-
"comment": "Add a rule that requires ECMAScript private class fields instead of TypeScript private fields.",
5+
"comment": "Add a rule that requires ECMAScript private syntax for class fields, methods, and accessors.",
66
"type": "minor"
77
}
88
]

eslint/eslint-config/flat/profile/_common.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ const commonConfig = [
221221
'@rushstack/no-new-null': 'warn',
222222

223223
// RATIONALE: See the @rushstack/eslint-plugin documentation
224-
'@rushstack/prefer-ecmascript-private-fields': 'warn',
224+
'@rushstack/prefer-ecmascript-private-members': 'warn',
225225

226226
// RATIONALE: See the @rushstack/eslint-plugin documentation
227227
'@rushstack/typedef-var': 'warn',

eslint/eslint-config/profile/_common.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ function buildRules(profile) {
237237
'@rushstack/no-new-null': 'warn',
238238

239239
// RATIONALE: See the @rushstack/eslint-plugin documentation
240-
'@rushstack/prefer-ecmascript-private-fields': 'warn',
240+
'@rushstack/prefer-ecmascript-private-members': 'warn',
241241

242242
// RATIONALE: See the @rushstack/eslint-plugin documentation
243243
'@rushstack/typedef-var': 'warn',

eslint/eslint-plugin/README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,18 +425,20 @@ enum E {
425425
let e: E._PrivateMember = E._PrivateMember; // okay, because _PrivateMember is declared by E
426426
```
427427

428-
## `@rushstack/prefer-ecmascript-private-fields`
428+
## `@rushstack/prefer-ecmascript-private-members`
429429

430-
Require ECMAScript private fields instead of fields declared with TypeScript's `private` modifier.
430+
Require ECMAScript private syntax for fields, methods, and accessors declared with TypeScript's `private`
431+
modifier.
431432

432433
#### Rule Details
433434

434-
ECMAScript `#` fields provide runtime privacy. TypeScript's `private` modifier is erased during compilation,
435-
allowing the field to be read or written through JavaScript, bracket notation, or type assertions.
435+
ECMAScript `#` members provide runtime privacy. TypeScript's `private` modifier is erased during compilation,
436+
allowing the member to be accessed through JavaScript, bracket notation, or type assertions.
436437

437-
This rule applies only to class fields. Private methods, accessors, and constructor parameter properties are
438-
not affected. The rule does not provide an autofix because converting a field requires updating every reference
439-
and may change runtime behavior for reflection or objects created without invoking the constructor.
438+
This rule applies to class fields, methods, and accessors. Private constructors and constructor parameter
439+
properties are not affected. The rule does not provide an autofix because converting a member requires updating
440+
every reference and may change runtime behavior for reflection or objects created without invoking the
441+
constructor.
440442

441443
#### Examples
442444

@@ -445,6 +447,7 @@ The following pattern is considered a problem:
445447
```ts
446448
class Example {
447449
private value: string = ''; // error
450+
private calculate(): number {} // error
448451
}
449452
```
450453

@@ -453,6 +456,7 @@ The following pattern is NOT considered a problem:
453456
```ts
454457
class Example {
455458
#value: string = '';
459+
#calculate(): number {}
456460
}
457461
```
458462

eslint/eslint-plugin/src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { normalizedImportsRule } from './normalized-imports';
1414
import { typedefVar } from './typedef-var';
1515
import { importRequiresChunkNameRule } from './import-requires-chunk-name';
1616
import { pairReactDomRenderUnmountRule } from './pair-react-dom-render-unmount';
17-
import { preferEcmascriptPrivateFieldsRule } from './prefer-ecmascript-private-fields';
17+
import { preferEcmascriptPrivateMembersRule } from './prefer-ecmascript-private-members';
1818

1919
interface IPlugin {
2020
rules: { [ruleName: string]: TSESLint.RuleModule<string, unknown[]> };
@@ -55,8 +55,8 @@ const plugin: IPlugin = {
5555
// Full name: "@rushstack/pair-react-dom-render-unmount"
5656
'pair-react-dom-render-unmount': pairReactDomRenderUnmountRule,
5757

58-
// Full name: "@rushstack/prefer-ecmascript-private-fields"
59-
'prefer-ecmascript-private-fields': preferEcmascriptPrivateFieldsRule
58+
// Full name: "@rushstack/prefer-ecmascript-private-members"
59+
'prefer-ecmascript-private-members': preferEcmascriptPrivateMembersRule
6060
}
6161
};
6262

eslint/eslint-plugin/src/prefer-ecmascript-private-fields.ts renamed to eslint/eslint-plugin/src/prefer-ecmascript-private-members.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33

44
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
55

6-
type MessageIds = 'use-ecmascript-private-field';
6+
type MessageIds = 'use-ecmascript-private-member';
77
type Options = [];
88

9-
const preferEcmascriptPrivateFieldsRule: TSESLint.RuleModule<MessageIds, Options> = {
9+
const preferEcmascriptPrivateMembersRule: TSESLint.RuleModule<MessageIds, Options> = {
1010
defaultOptions: [],
1111
meta: {
1212
type: 'suggestion',
1313
messages: {
14-
'use-ecmascript-private-field':
15-
'Use an ECMAScript private field ("#field") instead of the TypeScript "private" modifier.'
14+
'use-ecmascript-private-member':
15+
'Use ECMAScript private syntax ("#member") instead of the TypeScript "private" modifier.'
1616
},
1717
schema: [],
1818
docs: {
19-
description: 'Require ECMAScript private fields instead of TypeScript private class fields',
19+
description: 'Require ECMAScript private syntax for private class fields, methods, and accessors',
2020
recommended: 'recommended',
2121
url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin'
2222
} as TSESLint.RuleMetaDataDocs
@@ -26,11 +26,19 @@ const preferEcmascriptPrivateFieldsRule: TSESLint.RuleModule<MessageIds, Options
2626
if (node.accessibility === 'private') {
2727
context.report({
2828
node,
29-
messageId: 'use-ecmascript-private-field'
29+
messageId: 'use-ecmascript-private-member'
30+
});
31+
}
32+
},
33+
MethodDefinition(node: TSESTree.MethodDefinition): void {
34+
if (node.accessibility === 'private' && node.kind !== 'constructor') {
35+
context.report({
36+
node,
37+
messageId: 'use-ecmascript-private-member'
3038
});
3139
}
3240
}
3341
})
3442
};
3543

36-
export { preferEcmascriptPrivateFieldsRule };
44+
export { preferEcmascriptPrivateMembersRule };

eslint/eslint-plugin/src/test/prefer-ecmascript-private-fields.test.ts renamed to eslint/eslint-plugin/src/test/prefer-ecmascript-private-members.test.ts

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,78 @@
33

44
import type { RuleTester } from '@typescript-eslint/rule-tester';
55

6-
import { preferEcmascriptPrivateFieldsRule } from '../prefer-ecmascript-private-fields';
6+
import { preferEcmascriptPrivateMembersRule } from '../prefer-ecmascript-private-members';
77
import { getRuleTesterWithoutProject } from './ruleTester';
88

99
const ruleTester: RuleTester = getRuleTesterWithoutProject();
1010

11-
ruleTester.run('prefer-ecmascript-private-fields', preferEcmascriptPrivateFieldsRule, {
11+
ruleTester.run('prefer-ecmascript-private-members', preferEcmascriptPrivateMembersRule, {
1212
invalid: [
1313
{
1414
code: 'class Example { private value: string = ""; }',
15-
errors: [{ messageId: 'use-ecmascript-private-field' }]
15+
errors: [{ messageId: 'use-ecmascript-private-member' }]
1616
},
1717
{
1818
code: 'class Example { private static readonly values: Set<string> = new Set(); }',
19-
errors: [{ messageId: 'use-ecmascript-private-field' }]
19+
errors: [{ messageId: 'use-ecmascript-private-member' }]
2020
},
2121
{
2222
code: 'class Example { private optional?: string; private assigned!: string; }',
2323
errors: [
24-
{ messageId: 'use-ecmascript-private-field' },
25-
{ messageId: 'use-ecmascript-private-field' }
24+
{ messageId: 'use-ecmascript-private-member' },
25+
{ messageId: 'use-ecmascript-private-member' }
2626
]
2727
},
2828
{
2929
code: 'class Example { declare private value: string; }',
30-
errors: [{ messageId: 'use-ecmascript-private-field' }]
30+
errors: [{ messageId: 'use-ecmascript-private-member' }]
3131
},
3232
{
3333
code: 'class Example { private ["value"]: string = ""; }',
34-
errors: [{ messageId: 'use-ecmascript-private-field' }]
34+
errors: [{ messageId: 'use-ecmascript-private-member' }]
35+
},
36+
{
37+
code: 'class Example { private calculate(): number { return 1; } }',
38+
errors: [{ messageId: 'use-ecmascript-private-member' }]
39+
},
40+
{
41+
code: [
42+
'class Example {',
43+
' private get value(): string { return ""; }',
44+
' private set value(value: string) {}',
45+
'}'
46+
].join('\n'),
47+
errors: [
48+
{ messageId: 'use-ecmascript-private-member' },
49+
{ messageId: 'use-ecmascript-private-member' }
50+
]
3551
}
3652
],
3753
valid: [
3854
{
3955
code: 'class Example { #value: string = ""; static #values: Set<string> = new Set(); }'
4056
},
57+
{
58+
code: [
59+
'class Example {',
60+
' #calculate(): number { return 1; }',
61+
' get #value(): string { return ""; }',
62+
' set #value(value: string) {}',
63+
'}'
64+
].join('\n')
65+
},
4166
{
4267
code: 'class Example { public value: string = ""; protected otherValue: string = ""; }'
4368
},
4469
{
4570
code: [
4671
'class Example {',
47-
' private method(): void {}',
48-
' private get value(): string { return ""; }',
49-
' private set value(value: string) {}',
5072
' public constructor(private readonly parameter: string) {}',
5173
'}'
5274
].join('\n')
75+
},
76+
{
77+
code: 'class Example { private constructor() {} }'
5378
}
5479
]
5580
});

0 commit comments

Comments
 (0)