📄 explicit-member-accessibility¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 10 |
| 📦 Imports | 11 |
| 📐 Interfaces | 1 |
| 📑 Type Aliases | 3 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/explicit-member-accessibility.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'explicit-member-accessibility' |
meta.type |
'problem' |
meta.docs.description |
'Require explicit accessibility modifiers on class properties and methods' |
meta.fixable |
'code' |
meta.hasSuggestions |
true |
meta.messages.addExplicitAccessibility |
"Add '{{ type }}' accessibility modifier" |
meta.messages.missingAccessibility |
'Missing accessibility modifier on {{type}} {{name}}.' |
meta.messages.unwantedPublicAccessibility |
'Public accessibility modifier on {{type}} {{name}}.' |
meta.schema |
[ { type: 'object', $defs: { accessibilityLevel: { oneOf: [ { type: 'string', description: 'Always require an accesso... |
defaultOptions |
[{ accessibility: 'explicit' }] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESLint |
@typescript-eslint/utils |
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
AST_TOKEN_TYPES |
@typescript-eslint/utils |
createRule |
../util |
getNameFromMember |
../util |
nullThrows |
../util |
NullThrowsReasons |
../util |
getMemberHeadLoc |
../util/getMemberHeadLoc |
getParameterPropertyHeadLoc |
../util/getMemberHeadLoc |
rangeToLoc |
../util/rangeToLoc |
Functions¶
create(context: any, [option]: any): { 'MethodDefinition, TSAbstractMethodDefinition': (methodDe…¶
Parameters:
contextany[option]any
Returns: { 'MethodDefinition, TSAbstractMethodDefinition': (methodDefinition: TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition) => void; 'PropertyDefinition, TSAbstractPropertyDefinition, AccessorProperty, TSAbstractAccessorProperty': (propertyDefinition: TSESTree.AccessorProperty | TSESTree.PropertyDefinition | TSESTree.TSAbstractAccessorProperty | TSESTree.TSAbstractPropertyDefinition) => void; TSParameterProperty: (node: TSESTree.TSParameterProperty) => void; }
Calls:
getNameFromMember (from ../util)ignoredMethodNames.hasfindPublicKeywordcontext.reportrangeToLoc (from ../util/rangeToLoc)fixer.removeRangegetMemberHeadLoc (from ../util/getMemberHeadLoc)getMissingAccessibilitySuggestionscontext.sourceCode.getTokensstructuredClonecontext.sourceCode.getCommentsAfternullThrows (from ../util)context.sourceCode.getTokenAfterNullThrowsReasons.MissingTokenfixer.insertTextBeforefixgetParameterPropertyHeadLoc (from ../util/getMemberHeadLoc)
Internal Comments:
/**
* Checks if a method declaration has an accessibility modifier.
* @param methodDefinition The node representing a MethodDefinition.
*/
/**
* Returns an object containing a range that corresponds to the "public"
* keyword for a node, and the range that would need to be removed to
* remove the "public" keyword (including associated whitespace).
*/
// public /* Hi there! */ static foo() (x3)
// ^^^^^^^ (x6)
// public static foo() (x3)
/**
* Creates a fixer that adds an accessibility modifier keyword
*/
/**
* Checks if property has an accessibility modifier.
* @param propertyDefinition The node representing a PropertyDefinition.
*/
/**
* Checks that the parameter property has the desired accessibility modifiers set.
* @param node The node representing a Parameter Property
*/
Code
create(context, [option]) {
const baseCheck: AccessibilityLevel = option.accessibility ?? 'explicit';
const overrides = option.overrides ?? {};
const ctorCheck = overrides.constructors ?? baseCheck;
const accessorCheck = overrides.accessors ?? baseCheck;
const methodCheck = overrides.methods ?? baseCheck;
const propCheck = overrides.properties ?? baseCheck;
const paramPropCheck = overrides.parameterProperties ?? baseCheck;
const ignoredMethodNames = new Set(option.ignoredMethodNames ?? []);
/**
* Checks if a method declaration has an accessibility modifier.
* @param methodDefinition The node representing a MethodDefinition.
*/
function checkMethodAccessibilityModifier(
methodDefinition:
TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition,
): void {
if (methodDefinition.key.type === AST_NODE_TYPES.PrivateIdentifier) {
return;
}
let nodeType = 'method definition';
let check = baseCheck;
switch (methodDefinition.kind) {
case 'method':
check = methodCheck;
break;
case 'constructor':
check = ctorCheck;
break;
case 'get':
case 'set':
check = accessorCheck;
nodeType = `${methodDefinition.kind} property accessor`;
break;
}
const { name: methodName } = getNameFromMember(
methodDefinition,
context.sourceCode,
);
if (check === 'off' || ignoredMethodNames.has(methodName)) {
return;
}
if (
check === 'no-public' &&
methodDefinition.accessibility === 'public'
) {
const publicKeyword = findPublicKeyword(methodDefinition);
context.report({
loc: rangeToLoc(context.sourceCode, publicKeyword.range),
messageId: 'unwantedPublicAccessibility',
data: {
name: methodName,
type: nodeType,
},
fix: fixer => fixer.removeRange(publicKeyword.rangeToRemove),
});
} else if (check === 'explicit' && !methodDefinition.accessibility) {
context.report({
loc: getMemberHeadLoc(context.sourceCode, methodDefinition),
messageId: 'missingAccessibility',
data: {
name: methodName,
type: nodeType,
},
suggest: getMissingAccessibilitySuggestions(methodDefinition),
});
}
}
/**
* Returns an object containing a range that corresponds to the "public"
* keyword for a node, and the range that would need to be removed to
* remove the "public" keyword (including associated whitespace).
*/
function findPublicKeyword(
node:
| TSESTree.AccessorProperty
| TSESTree.MethodDefinition
| TSESTree.PropertyDefinition
| TSESTree.TSAbstractAccessorProperty
| TSESTree.TSAbstractMethodDefinition
| TSESTree.TSAbstractPropertyDefinition
| TSESTree.TSParameterProperty,
): { range: TSESLint.AST.Range; rangeToRemove: TSESLint.AST.Range } {
const tokens = context.sourceCode.getTokens(node);
let rangeToRemove!: TSESLint.AST.Range;
let keywordRange!: TSESLint.AST.Range;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (
token.type === AST_TOKEN_TYPES.Keyword &&
token.value === 'public'
) {
keywordRange = structuredClone(token.range);
const commentsAfterPublicKeyword =
context.sourceCode.getCommentsAfter(token);
if (commentsAfterPublicKeyword.length) {
// public /* Hi there! */ static foo()
// ^^^^^^^
rangeToRemove = [
token.range[0],
commentsAfterPublicKeyword[0].range[0],
];
break;
} else {
// public static foo()
// ^^^^^^^
rangeToRemove = [token.range[0], tokens[i + 1].range[0]];
break;
}
}
}
return { range: keywordRange, rangeToRemove };
}
/**
* Creates a fixer that adds an accessibility modifier keyword
*/
function getMissingAccessibilitySuggestions(
node:
| TSESTree.AccessorProperty
| TSESTree.MethodDefinition
| TSESTree.PropertyDefinition
| TSESTree.TSAbstractAccessorProperty
| TSESTree.TSAbstractMethodDefinition
| TSESTree.TSAbstractPropertyDefinition
| TSESTree.TSParameterProperty,
): TSESLint.ReportSuggestionArray<MessageIds> {
function fix(
accessibility: TSESTree.Accessibility,
fixer: TSESLint.RuleFixer,
): TSESLint.RuleFix | null {
if (node.decorators.length) {
const lastDecorator = node.decorators[node.decorators.length - 1];
const nextToken = nullThrows(
context.sourceCode.getTokenAfter(lastDecorator),
NullThrowsReasons.MissingToken('token', 'last decorator'),
);
return fixer.insertTextBefore(nextToken, `${accessibility} `);
}
return fixer.insertTextBefore(node, `${accessibility} `);
}
return [
{
messageId: 'addExplicitAccessibility',
data: { type: 'public' },
fix: fixer => fix('public', fixer),
},
{
messageId: 'addExplicitAccessibility',
data: { type: 'private' },
fix: fixer => fix('private', fixer),
},
{
messageId: 'addExplicitAccessibility',
data: { type: 'protected' },
fix: fixer => fix('protected', fixer),
},
];
}
/**
* Checks if property has an accessibility modifier.
* @param propertyDefinition The node representing a PropertyDefinition.
*/
function checkPropertyAccessibilityModifier(
propertyDefinition:
| TSESTree.AccessorProperty
| TSESTree.PropertyDefinition
| TSESTree.TSAbstractAccessorProperty
| TSESTree.TSAbstractPropertyDefinition,
): void {
if (propertyDefinition.key.type === AST_NODE_TYPES.PrivateIdentifier) {
return;
}
const nodeType = 'class property';
const { name: propertyName } = getNameFromMember(
propertyDefinition,
context.sourceCode,
);
if (
propCheck === 'no-public' &&
propertyDefinition.accessibility === 'public'
) {
const publicKeywordRange = findPublicKeyword(propertyDefinition);
context.report({
loc: rangeToLoc(context.sourceCode, publicKeywordRange.range),
messageId: 'unwantedPublicAccessibility',
data: {
name: propertyName,
type: nodeType,
},
fix: fixer => fixer.removeRange(publicKeywordRange.rangeToRemove),
});
} else if (
propCheck === 'explicit' &&
!propertyDefinition.accessibility
) {
context.report({
loc: getMemberHeadLoc(context.sourceCode, propertyDefinition),
messageId: 'missingAccessibility',
data: {
name: propertyName,
type: nodeType,
},
suggest: getMissingAccessibilitySuggestions(propertyDefinition),
});
}
}
/**
* Checks that the parameter property has the desired accessibility modifiers set.
* @param node The node representing a Parameter Property
*/
function checkParameterPropertyAccessibilityModifier(
node: TSESTree.TSParameterProperty,
): void {
const nodeType = 'parameter property';
const nodeName =
node.parameter.type === AST_NODE_TYPES.Identifier
? node.parameter.name
: node.parameter.left.name;
switch (paramPropCheck) {
case 'explicit': {
if (!node.accessibility) {
context.report({
loc: getParameterPropertyHeadLoc(
context.sourceCode,
node,
nodeName,
),
messageId: 'missingAccessibility',
data: {
name: nodeName,
type: nodeType,
},
suggest: getMissingAccessibilitySuggestions(node),
});
}
break;
}
case 'no-public': {
if (node.accessibility === 'public' && node.readonly) {
const publicKeyword = findPublicKeyword(node);
context.report({
loc: rangeToLoc(context.sourceCode, publicKeyword.range),
messageId: 'unwantedPublicAccessibility',
data: {
name: nodeName,
type: nodeType,
},
fix: fixer => fixer.removeRange(publicKeyword.rangeToRemove),
});
}
break;
}
}
}
return {
'MethodDefinition, TSAbstractMethodDefinition':
checkMethodAccessibilityModifier,
'PropertyDefinition, TSAbstractPropertyDefinition, AccessorProperty, TSAbstractAccessorProperty':
checkPropertyAccessibilityModifier,
TSParameterProperty: checkParameterPropertyAccessibilityModifier,
};
}
Internal helpers¶
Declared inside another function in this file.
checkMethodAccessibilityModifier(methodDefinition: TSESTree.MethodDefinition | TSESTree.TS…): void¶
Checks if a method declaration has an accessibility modifier.
Parameters:
methodDefinitionany: The node representing a MethodDefinition.
Raw JSDoc
Calls:
getNameFromMember (from ../util)ignoredMethodNames.hasfindPublicKeywordcontext.reportrangeToLoc (from ../util/rangeToLoc)fixer.removeRangegetMemberHeadLoc (from ../util/getMemberHeadLoc)getMissingAccessibilitySuggestions
Code
function checkMethodAccessibilityModifier(
methodDefinition:
TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition,
): void {
if (methodDefinition.key.type === AST_NODE_TYPES.PrivateIdentifier) {
return;
}
let nodeType = 'method definition';
let check = baseCheck;
switch (methodDefinition.kind) {
case 'method':
check = methodCheck;
break;
case 'constructor':
check = ctorCheck;
break;
case 'get':
case 'set':
check = accessorCheck;
nodeType = `${methodDefinition.kind} property accessor`;
break;
}
const { name: methodName } = getNameFromMember(
methodDefinition,
context.sourceCode,
);
if (check === 'off' || ignoredMethodNames.has(methodName)) {
return;
}
if (
check === 'no-public' &&
methodDefinition.accessibility === 'public'
) {
const publicKeyword = findPublicKeyword(methodDefinition);
context.report({
loc: rangeToLoc(context.sourceCode, publicKeyword.range),
messageId: 'unwantedPublicAccessibility',
data: {
name: methodName,
type: nodeType,
},
fix: fixer => fixer.removeRange(publicKeyword.rangeToRemove),
});
} else if (check === 'explicit' && !methodDefinition.accessibility) {
context.report({
loc: getMemberHeadLoc(context.sourceCode, methodDefinition),
messageId: 'missingAccessibility',
data: {
name: methodName,
type: nodeType,
},
suggest: getMissingAccessibilitySuggestions(methodDefinition),
});
}
}
findPublicKeyword(node: | TSESTree.AccessorProperty | TSESTree.…): { range: TSESLint.AST.Range; rangeToRemove: TSESLint.AST.Ra…¶
Returns an object containing a range that corresponds to the "public" keyword for a node, and the range that would need to be removed to remove the "public" keyword (including associated whitespace).
Raw JSDoc
Calls:
context.sourceCode.getTokensstructuredClonecontext.sourceCode.getCommentsAfter
Internal Comments:
Code
function findPublicKeyword(
node:
| TSESTree.AccessorProperty
| TSESTree.MethodDefinition
| TSESTree.PropertyDefinition
| TSESTree.TSAbstractAccessorProperty
| TSESTree.TSAbstractMethodDefinition
| TSESTree.TSAbstractPropertyDefinition
| TSESTree.TSParameterProperty,
): { range: TSESLint.AST.Range; rangeToRemove: TSESLint.AST.Range } {
const tokens = context.sourceCode.getTokens(node);
let rangeToRemove!: TSESLint.AST.Range;
let keywordRange!: TSESLint.AST.Range;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (
token.type === AST_TOKEN_TYPES.Keyword &&
token.value === 'public'
) {
keywordRange = structuredClone(token.range);
const commentsAfterPublicKeyword =
context.sourceCode.getCommentsAfter(token);
if (commentsAfterPublicKeyword.length) {
// public /* Hi there! */ static foo()
// ^^^^^^^
rangeToRemove = [
token.range[0],
commentsAfterPublicKeyword[0].range[0],
];
break;
} else {
// public static foo()
// ^^^^^^^
rangeToRemove = [token.range[0], tokens[i + 1].range[0]];
break;
}
}
}
return { range: keywordRange, rangeToRemove };
}
getMissingAccessibilitySuggestions(node: | TSESTree.AccessorProperty | TSESTree.…): TSESLint.ReportSuggestionArray<MessageIds>¶
Creates a fixer that adds an accessibility modifier keyword
Calls:
nullThrows (from ../util)context.sourceCode.getTokenAfterNullThrowsReasons.MissingTokenfixer.insertTextBeforefix
Code
function getMissingAccessibilitySuggestions(
node:
| TSESTree.AccessorProperty
| TSESTree.MethodDefinition
| TSESTree.PropertyDefinition
| TSESTree.TSAbstractAccessorProperty
| TSESTree.TSAbstractMethodDefinition
| TSESTree.TSAbstractPropertyDefinition
| TSESTree.TSParameterProperty,
): TSESLint.ReportSuggestionArray<MessageIds> {
function fix(
accessibility: TSESTree.Accessibility,
fixer: TSESLint.RuleFixer,
): TSESLint.RuleFix | null {
if (node.decorators.length) {
const lastDecorator = node.decorators[node.decorators.length - 1];
const nextToken = nullThrows(
context.sourceCode.getTokenAfter(lastDecorator),
NullThrowsReasons.MissingToken('token', 'last decorator'),
);
return fixer.insertTextBefore(nextToken, `${accessibility} `);
}
return fixer.insertTextBefore(node, `${accessibility} `);
}
return [
{
messageId: 'addExplicitAccessibility',
data: { type: 'public' },
fix: fixer => fix('public', fixer),
},
{
messageId: 'addExplicitAccessibility',
data: { type: 'private' },
fix: fixer => fix('private', fixer),
},
{
messageId: 'addExplicitAccessibility',
data: { type: 'protected' },
fix: fixer => fix('protected', fixer),
},
];
}
getMissingAccessibilitySuggestions.fix(accessibility: TSESTree.Accessibility, fixer: TSESLint.RuleFixer): TSESLint.RuleFix | null¶
Parameters:
accessibilityTSESTree.AccessibilityfixerTSESLint.RuleFixer
Returns: TSESLint.RuleFix | null
Calls:
nullThrows (from ../util)context.sourceCode.getTokenAfterNullThrowsReasons.MissingTokenfixer.insertTextBefore
Code
function fix(
accessibility: TSESTree.Accessibility,
fixer: TSESLint.RuleFixer,
): TSESLint.RuleFix | null {
if (node.decorators.length) {
const lastDecorator = node.decorators[node.decorators.length - 1];
const nextToken = nullThrows(
context.sourceCode.getTokenAfter(lastDecorator),
NullThrowsReasons.MissingToken('token', 'last decorator'),
);
return fixer.insertTextBefore(nextToken, `${accessibility} `);
}
return fixer.insertTextBefore(node, `${accessibility} `);
}
getMissingAccessibilitySuggestions.fix(fixer: any): any¶
Parameters:
fixerany
Returns: any
Calls:
fix
getMissingAccessibilitySuggestions.fix(fixer: any): any¶
Parameters:
fixerany
Returns: any
Calls:
fix
getMissingAccessibilitySuggestions.fix(fixer: any): any¶
Parameters:
fixerany
Returns: any
Calls:
fix
checkPropertyAccessibilityModifier(propertyDefinition: | TSESTree.AccessorProperty | TSESTree.…): void¶
Checks if property has an accessibility modifier.
Parameters:
propertyDefinitionany: The node representing a PropertyDefinition.
Raw JSDoc
Calls:
getNameFromMember (from ../util)findPublicKeywordcontext.reportrangeToLoc (from ../util/rangeToLoc)fixer.removeRangegetMemberHeadLoc (from ../util/getMemberHeadLoc)getMissingAccessibilitySuggestions
Code
function checkPropertyAccessibilityModifier(
propertyDefinition:
| TSESTree.AccessorProperty
| TSESTree.PropertyDefinition
| TSESTree.TSAbstractAccessorProperty
| TSESTree.TSAbstractPropertyDefinition,
): void {
if (propertyDefinition.key.type === AST_NODE_TYPES.PrivateIdentifier) {
return;
}
const nodeType = 'class property';
const { name: propertyName } = getNameFromMember(
propertyDefinition,
context.sourceCode,
);
if (
propCheck === 'no-public' &&
propertyDefinition.accessibility === 'public'
) {
const publicKeywordRange = findPublicKeyword(propertyDefinition);
context.report({
loc: rangeToLoc(context.sourceCode, publicKeywordRange.range),
messageId: 'unwantedPublicAccessibility',
data: {
name: propertyName,
type: nodeType,
},
fix: fixer => fixer.removeRange(publicKeywordRange.rangeToRemove),
});
} else if (
propCheck === 'explicit' &&
!propertyDefinition.accessibility
) {
context.report({
loc: getMemberHeadLoc(context.sourceCode, propertyDefinition),
messageId: 'missingAccessibility',
data: {
name: propertyName,
type: nodeType,
},
suggest: getMissingAccessibilitySuggestions(propertyDefinition),
});
}
}
checkParameterPropertyAccessibilityModifier(node: TSESTree.TSParameterProperty): void¶
Checks that the parameter property has the desired accessibility modifiers set.
Parameters:
nodeany: The node representing a Parameter Property
Raw JSDoc
Calls:
context.reportgetParameterPropertyHeadLoc (from ../util/getMemberHeadLoc)getMissingAccessibilitySuggestionsfindPublicKeywordrangeToLoc (from ../util/rangeToLoc)fixer.removeRange
Code
function checkParameterPropertyAccessibilityModifier(
node: TSESTree.TSParameterProperty,
): void {
const nodeType = 'parameter property';
const nodeName =
node.parameter.type === AST_NODE_TYPES.Identifier
? node.parameter.name
: node.parameter.left.name;
switch (paramPropCheck) {
case 'explicit': {
if (!node.accessibility) {
context.report({
loc: getParameterPropertyHeadLoc(
context.sourceCode,
node,
nodeName,
),
messageId: 'missingAccessibility',
data: {
name: nodeName,
type: nodeType,
},
suggest: getMissingAccessibilitySuggestions(node),
});
}
break;
}
case 'no-public': {
if (node.accessibility === 'public' && node.readonly) {
const publicKeyword = findPublicKeyword(node);
context.report({
loc: rangeToLoc(context.sourceCode, publicKeyword.range),
messageId: 'unwantedPublicAccessibility',
data: {
name: nodeName,
type: nodeType,
},
fix: fixer => fixer.removeRange(publicKeyword.rangeToRemove),
});
}
break;
}
}
}
Interfaces¶
Config¶
Interface Code
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
accessibility |
AccessibilityLevel |
✓ | not shown |
ignoredMethodNames |
string[] |
✓ | not shown |
overrides |
{ accessors?: AccessibilityLevel; constructors?: AccessibilityLevel; methods?... |
✓ | not shown |
Type Aliases¶
AccessibilityLevel¶
type AccessibilityLevel = | 'explicit' // require an accessor (including public)
| 'no-public' // don't require public
| 'off';
Options¶
MessageIds¶
type MessageIds = | 'addExplicitAccessibility'
| 'missingAccessibility'
| 'unwantedPublicAccessibility';
Generated by Syntax Scribe