📄 class-literal-property-style¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 6 |
| 📦 Imports | 10 |
| 📐 Interfaces | 2 |
| 📑 Type Aliases | 2 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/class-literal-property-style.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'class-literal-property-style' |
meta.type |
'problem' |
meta.docs.description |
'Enforce that literals on classes are exposed in a consistent style' |
meta.docs.recommended |
'stylistic' |
meta.hasSuggestions |
true |
meta.messages.preferFieldStyle |
'Literals should be exposed using readonly fields.' |
meta.messages.preferFieldStyleSuggestion |
'Replace the literals with readonly fields.' |
meta.messages.preferGetterStyle |
'Literals should be exposed using getters.' |
meta.messages.preferGetterStyleSuggestion |
'Replace the literals with getters.' |
meta.schema |
[ { type: 'string', description: 'Which literal class member syntax to prefer.', enum: ['fields', 'getters'], }, ] |
defaultOptions |
['fields'] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESLint |
@typescript-eslint/utils |
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
createRule |
../util |
getFixOrSuggest |
../util |
getStaticMemberAccessValue |
../util |
isAssignee |
../util |
isFunction |
../util |
isStaticMemberAccessOfValue |
../util |
nullThrows |
../util |
Functions¶
create(context: any, [style]: any): { ClassBody: () => void; 'ClassBody:exit': () => void; 'Met…¶
Parameters:
contextany[style]any
Returns: { ClassBody: () => void; 'ClassBody:exit': () => void; 'MethodDefinition[kind="constructor"] ThisExpression'(node: TSESTree.ThisExpression): void; PropertyDefinition(node: any): void; MethodDefinition(node: any): void; }
Calls:
propertiesInfoStack.pushnullThrows (from ../util)propertiesInfoStack.popproperties.forEachisSupportedLiteralgetStaticMemberAccessValue (from ../util)excludeSet.hascontext.reportcontext.sourceCode.getTextprintNodeModifiersfixer.replaceTextisAssignee (from ../util)excludeSet.addnode.parent.body.someisStaticMemberAccessOfValue (from ../util)getFixOrSuggest (from ../util)context.sourceCode.getTokenBeforecontext.sourceCode .getText() .sliceisFunction (from ../util)excludeAssignedPropertyproperties.push
Code
create(context, [style]) {
const propertiesInfoStack: PropertiesInfo[] = [];
function enterClassBody(): void {
propertiesInfoStack.push({
excludeSet: new Set(),
properties: [],
});
}
function exitClassBody(): void {
const { excludeSet, properties } = nullThrows(
propertiesInfoStack.pop(),
'Stack should exist on class exit',
);
properties.forEach(node => {
const { value } = node;
if (!value || !isSupportedLiteral(value)) {
return;
}
const name = getStaticMemberAccessValue(node, context);
if (name && excludeSet.has(name)) {
return;
}
context.report({
node: node.key,
messageId: 'preferGetterStyle',
suggest: [
{
messageId: 'preferGetterStyleSuggestion',
fix(fixer): TSESLint.RuleFix {
const name = context.sourceCode.getText(node.key);
let text = '';
text += printNodeModifiers(node, 'get');
text += node.computed ? `[${name}]` : name;
text += `() { return ${context.sourceCode.getText(value)}; }`;
return fixer.replaceText(node, text);
},
},
],
});
});
}
function excludeAssignedProperty(node: TSESTree.MemberExpression): void {
if (isAssignee(node)) {
const { excludeSet } =
propertiesInfoStack[propertiesInfoStack.length - 1];
const name = getStaticMemberAccessValue(node, context);
if (name) {
excludeSet.add(name);
}
}
}
return {
...(style === 'fields' && {
MethodDefinition(node): void {
if (
node.kind !== 'get' ||
node.override ||
!node.value.body ||
node.value.body.body.length === 0
) {
return;
}
const [statement] = node.value.body.body;
if (statement.type !== AST_NODE_TYPES.ReturnStatement) {
return;
}
const { argument } = statement;
if (!argument || !isSupportedLiteral(argument)) {
return;
}
const name = getStaticMemberAccessValue(node, context);
const hasDuplicateKeySetter =
name &&
node.parent.body.some(element => {
return (
element.type === AST_NODE_TYPES.MethodDefinition &&
element.kind === 'set' &&
isStaticMemberAccessOfValue(element, context, name)
);
});
if (hasDuplicateKeySetter) {
return;
}
const getterBody = node.value.body;
context.report({
node: node.key,
messageId: 'preferFieldStyle',
...getFixOrSuggest({
fixOrSuggest: node.decorators.length === 0 ? 'suggest' : 'none',
suggestion: {
messageId: 'preferFieldStyleSuggestion',
fix(fixer): TSESLint.RuleFix {
const name = context.sourceCode.getText(node.key);
const closingParen = nullThrows(
context.sourceCode.getTokenBefore(
node.value.returnType ?? getterBody,
),
'Getter should have a closing parenthesis.',
);
const betweenParensAndBody = context.sourceCode
.getText()
.slice(closingParen.range[1], getterBody.range[0]);
let text = '';
text += printNodeModifiers(node, 'readonly');
text += node.computed ? `[${name}]` : name;
text += betweenParensAndBody;
text += `= ${context.sourceCode.getText(argument)};`;
return fixer.replaceText(node, text);
},
},
}),
});
},
}),
...(style === 'getters' && {
ClassBody: enterClassBody,
'ClassBody:exit': exitClassBody,
'MethodDefinition[kind="constructor"] ThisExpression'(
node: TSESTree.ThisExpression,
): void {
if (node.parent.type === AST_NODE_TYPES.MemberExpression) {
let parent: TSESTree.Node | undefined = node.parent;
while (!isFunction(parent)) {
parent = parent.parent;
}
if (
parent.parent.type === AST_NODE_TYPES.MethodDefinition &&
parent.parent.kind === 'constructor'
) {
excludeAssignedProperty(node.parent);
}
}
},
PropertyDefinition(node): void {
if (!node.readonly || node.declare || node.override) {
return;
}
const { properties } =
propertiesInfoStack[propertiesInfoStack.length - 1];
properties.push(node);
},
}),
};
}
printNodeModifiers(node: NodeWithModifiers, final: 'get' | 'readonly'): string¶
Parameters:
nodeNodeWithModifiersfinal'get' | 'readonly'
Returns: string
Calls:
`${node.accessibility ?? ''}${ node.static ? ' static' : '' } ${final}.trimStart`
Code
isSupportedLiteral(node: TSESTree.Node): node is TSESTree.LiteralExpression¶
Parameters:
nodeTSESTree.Node
Returns: node is TSESTree.LiteralExpression
Code
(
node: TSESTree.Node,
): node is TSESTree.LiteralExpression => {
switch (node.type) {
case AST_NODE_TYPES.Literal:
return true;
case AST_NODE_TYPES.TaggedTemplateExpression:
return node.quasi.quasis.length === 1;
case AST_NODE_TYPES.TemplateLiteral:
return node.quasis.length === 1;
default:
return false;
}
}
Internal helpers¶
Declared inside another function in this file.
enterClassBody(): void¶
Returns: void
Calls:
propertiesInfoStack.push
Code
exitClassBody(): void¶
Returns: void
Calls:
nullThrows (from ../util)propertiesInfoStack.popproperties.forEachisSupportedLiteralgetStaticMemberAccessValue (from ../util)excludeSet.hascontext.reportcontext.sourceCode.getTextprintNodeModifiersfixer.replaceText
Code
function exitClassBody(): void {
const { excludeSet, properties } = nullThrows(
propertiesInfoStack.pop(),
'Stack should exist on class exit',
);
properties.forEach(node => {
const { value } = node;
if (!value || !isSupportedLiteral(value)) {
return;
}
const name = getStaticMemberAccessValue(node, context);
if (name && excludeSet.has(name)) {
return;
}
context.report({
node: node.key,
messageId: 'preferGetterStyle',
suggest: [
{
messageId: 'preferGetterStyleSuggestion',
fix(fixer): TSESLint.RuleFix {
const name = context.sourceCode.getText(node.key);
let text = '';
text += printNodeModifiers(node, 'get');
text += node.computed ? `[${name}]` : name;
text += `() { return ${context.sourceCode.getText(value)}; }`;
return fixer.replaceText(node, text);
},
},
],
});
});
}
excludeAssignedProperty(node: TSESTree.MemberExpression): void¶
Parameters:
nodeTSESTree.MemberExpression
Returns: void
Calls:
isAssignee (from ../util)getStaticMemberAccessValue (from ../util)excludeSet.add
Code
Interfaces¶
NodeWithModifiers¶
Interface Code
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
accessibility |
TSESTree.Accessibility |
✓ | not shown |
static |
boolean |
✗ | not shown |
PropertiesInfo¶
Interface Code
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
excludeSet |
Set<string \| symbol> |
✗ | not shown |
properties |
TSESTree.PropertyDefinition[] |
✗ | not shown |
Type Aliases¶
Options¶
MessageIds¶
type MessageIds = | 'preferFieldStyle'
| 'preferFieldStyleSuggestion'
| 'preferGetterStyle'
| 'preferGetterStyleSuggestion';
Generated by Syntax Scribe