β¬ οΈ Back to Table of Contents
π prefer-readonly¶
π Analysis Summary¶
| Metric | Count |
|---|---|
| π§ Functions | 19 |
| π§± Classes | 1 |
| π¦ Imports | 10 |
| π Variables & Constants | 2 |
| π Type Aliases | 3 |
| π― Enums | 1 |
π Table of Contents¶
π οΈ File Location:¶
π packages/eslint-plugin/src/rules/prefer-readonly.ts
π€ Default Export¶
| Property | Value |
|---|---|
name |
'prefer-readonly' |
meta.type |
'suggestion' |
meta.docs.description |
"Require private members to be marked as readonly if they're never modified outside of the constructor" |
meta.docs.requiresTypeChecking |
true |
meta.fixable |
'code' |
meta.messages.preferReadonly |
"Member '{{name}}' is never reassigned; mark it as readonly." |
meta.schema |
[ { type: 'object', additionalProperties: false, properties: { onlyInlineLambdas: { type: 'boolean', description: 'Wh... |
defaultOptions |
[{ onlyInlineLambdas: false }] |
Entry point: create β documented under Functions.
π¦ Imports¶
| Name | Source |
|---|---|
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
ASTUtils |
@typescript-eslint/utils |
createRule |
../util |
getStaticMemberAccessValue |
../util |
getParserServices |
../util |
nullThrows |
../util |
typeIsOrHasBaseType |
../util |
getMemberHeadLoc |
../util/getMemberHeadLoc |
getParameterPropertyHeadLoc |
../util/getMemberHeadLoc |
Variables & Constants¶
| Name | Type | Kind | Value | Exported |
|---|---|---|---|---|
OUTSIDE_CONSTRUCTOR |
-1 |
const | -1 |
β |
DIRECTLY_INSIDE_CONSTRUCTOR |
0 |
const | 0 |
β |
Functions¶
create(context: any, [{ onlyInlineLambdas }]: any): { [x: string]: (node: TSESTree.ArrowFunctionExpression | TS⦶
Parameters:
contextany[{ onlyInlineLambdas }]any
Returns: { [x: string]: (node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.MethodDefinition) => void; 'ClassDeclaration, ClassExpression'(node: TSESTree.ClassDeclaration | TSESTree.ClassExpression): void; 'ClassDeclaration, ClassExpression:exit'(): void; MemberExpression(node: any): void; }
Calls:
getParserServices (from ../util)services.program.getTypeCheckerts.isBinaryExpressionhandleParentBinaryExpressionts.isDeleteExpressionisDestructuringAssignmentclassScope.addVariableModificationts.isPostfixUnaryExpressionts.isPrefixUnaryExpressionhandleParentPostfixOrPrefixUnaryExpressiontsutils.isAssignmentKindts.isObjectLiteralExpressionts.isArrayLiteralExpressionts.isSpreadAssignmentts.isSpreadElementts.isPropertyAccessExpressionservices.esTreeNodeToTSNodeMap.getts.isConstructorDeclarationtsutils.isFunctionScopeBoundaryservices.tsNodeToESTreeNodeMap.getchecker.typeToStringtsutils.isTypeFlagSetcontext.sourceCode.getScopeASTUtils.findVariablevariable.defs.findservices.getTypeAtLocationASTUtils.isConstructorclassScopeStack[classScopeStack.length - 1].exitConstructorisFunctionScopeBoundaryInStackclassScopeStack[classScopeStack.length - 1].exitNonConstructorclassScopeStack.pushnullThrows (from ../util)classScopeStack.popfinalizedClassScope.finalizeUnmodifiedPrivateNonReadonlysgetEsNodesFromViolatingNodecomplex_call_7065getMemberHeadLoc (from ../util/getMemberHeadLoc)getParameterPropertyHeadLoc (from ../util/getMemberHeadLoc)complex_call_7817finalizedClassScope.memberHasConstructorModificationstsutils.isLiteralTypegetTypeAnnotationForViolatingNodecontext.reportcontext.sourceCode.getTextcontext.sourceCode.getTokenBeforefixer.insertTextBeforefixer.insertTextAfterclassScopeStack[classScopeStack.length - 1].enterConstructorclassScopeStack[classScopeStack.length - 1].enterNonConstructorhandlePropertyAccessExpressionts.isElementAccessExpressiongetStaticMemberAccessValue (from ../util)classScope.addVariableModificationByName
Internal Comments:
// verify the about-to-be-added type annotation is in-scope
// if the RHS is a literal, its type would be narrowed, while the
// type of the initializer (which isn't `readonly`) would be the
// widened type
Code
create(context, [{ onlyInlineLambdas }]) {
const services = getParserServices(context);
const checker = services.program.getTypeChecker();
const classScopeStack: ClassScope[] = [];
function handlePropertyAccessExpression(
node: ts.PropertyAccessExpression,
parent: ts.Node,
classScope: ClassScope,
): void {
if (ts.isBinaryExpression(parent)) {
handleParentBinaryExpression(node, parent, classScope);
return;
}
if (ts.isDeleteExpression(parent) || isDestructuringAssignment(node)) {
classScope.addVariableModification(node);
return;
}
if (
ts.isPostfixUnaryExpression(parent) ||
ts.isPrefixUnaryExpression(parent)
) {
handleParentPostfixOrPrefixUnaryExpression(parent, classScope);
}
}
function handleParentBinaryExpression(
node: ts.PropertyAccessExpression,
parent: ts.BinaryExpression,
classScope: ClassScope,
): void {
if (
parent.left === node &&
tsutils.isAssignmentKind(parent.operatorToken.kind)
) {
classScope.addVariableModification(node);
}
}
function handleParentPostfixOrPrefixUnaryExpression(
node: ts.PostfixUnaryExpression | ts.PrefixUnaryExpression,
classScope: ClassScope,
): void {
if (
node.operator === ts.SyntaxKind.PlusPlusToken ||
node.operator === ts.SyntaxKind.MinusMinusToken
) {
classScope.addVariableModification(
node.operand as ts.PropertyAccessExpression,
);
}
}
function isDestructuringAssignment(
node: ts.PropertyAccessExpression,
): boolean {
let current = node.parent as ts.Node | undefined;
while (current) {
const parent = current.parent;
if (
ts.isObjectLiteralExpression(parent) ||
ts.isArrayLiteralExpression(parent) ||
ts.isSpreadAssignment(parent) ||
(ts.isSpreadElement(parent) &&
ts.isArrayLiteralExpression(parent.parent))
) {
current = parent;
} else if (
ts.isBinaryExpression(parent) &&
!ts.isPropertyAccessExpression(current)
) {
return (
parent.left === current &&
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken
);
} else {
break;
}
}
return false;
}
function isFunctionScopeBoundaryInStack(
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| TSESTree.MethodDefinition,
): boolean {
if (classScopeStack.length === 0) {
return false;
}
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
if (ts.isConstructorDeclaration(tsNode)) {
return false;
}
return tsutils.isFunctionScopeBoundary(tsNode);
}
function getEsNodesFromViolatingNode(
violatingNode: ParameterOrPropertyDeclaration,
): { esNode: TSESTree.Node; nameNode: TSESTree.Node } {
return {
esNode: services.tsNodeToESTreeNodeMap.get(violatingNode),
nameNode: services.tsNodeToESTreeNodeMap.get(violatingNode.name),
};
}
function getTypeAnnotationForViolatingNode(
node: TSESTree.Node,
type: ts.Type,
initializerType: ts.Type,
) {
const annotation = checker.typeToString(type);
// verify the about-to-be-added type annotation is in-scope
if (tsutils.isTypeFlagSet(initializerType, ts.TypeFlags.EnumLiteral)) {
const scope = context.sourceCode.getScope(node);
const variable = ASTUtils.findVariable(scope, annotation);
if (variable == null) {
return null;
}
const definition = variable.defs.find(def => def.isTypeDefinition);
if (definition == null) {
return null;
}
const definitionType = services.getTypeAtLocation(definition.node);
if (definitionType !== type) {
return null;
}
}
return annotation;
}
return {
[`${functionScopeBoundaries}:exit`](
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| TSESTree.MethodDefinition,
): void {
if (ASTUtils.isConstructor(node)) {
classScopeStack[classScopeStack.length - 1].exitConstructor();
} else if (isFunctionScopeBoundaryInStack(node)) {
classScopeStack[classScopeStack.length - 1].exitNonConstructor();
}
},
'ClassDeclaration, ClassExpression'(
node: TSESTree.ClassDeclaration | TSESTree.ClassExpression,
): void {
classScopeStack.push(
new ClassScope(
checker,
services.esTreeNodeToTSNodeMap.get(node),
onlyInlineLambdas,
),
);
},
'ClassDeclaration, ClassExpression:exit'(): void {
const finalizedClassScope = nullThrows(
classScopeStack.pop(),
'Stack should exist on class exit',
);
for (const violatingNode of finalizedClassScope.finalizeUnmodifiedPrivateNonReadonlys()) {
const { esNode, nameNode } =
getEsNodesFromViolatingNode(violatingNode);
const reportNodeOrLoc:
{ loc: TSESTree.SourceLocation } | { node: TSESTree.Node } =
(() => {
switch (esNode.type) {
case AST_NODE_TYPES.MethodDefinition:
case AST_NODE_TYPES.PropertyDefinition:
case AST_NODE_TYPES.TSAbstractMethodDefinition:
return { loc: getMemberHeadLoc(context.sourceCode, esNode) };
case AST_NODE_TYPES.TSParameterProperty:
return {
loc: getParameterPropertyHeadLoc(
context.sourceCode,
esNode,
(nameNode as TSESTree.Identifier).name,
),
};
default:
return { node: esNode };
}
})();
const typeAnnotation = (() => {
if (esNode.type !== AST_NODE_TYPES.PropertyDefinition) {
return null;
}
if (esNode.typeAnnotation || !esNode.value) {
return null;
}
if (nameNode.type !== AST_NODE_TYPES.Identifier) {
return null;
}
const hasConstructorModifications =
finalizedClassScope.memberHasConstructorModifications(
nameNode.name,
);
if (!hasConstructorModifications) {
return null;
}
const violatingType = services.getTypeAtLocation(esNode);
const initializerType = services.getTypeAtLocation(esNode.value);
// if the RHS is a literal, its type would be narrowed, while the
// type of the initializer (which isn't `readonly`) would be the
// widened type
if (initializerType === violatingType) {
return null;
}
if (!tsutils.isLiteralType(initializerType)) {
return null;
}
return getTypeAnnotationForViolatingNode(
esNode,
violatingType,
initializerType,
);
})();
context.report({
...reportNodeOrLoc,
messageId: 'preferReadonly',
data: {
name: context.sourceCode.getText(nameNode),
},
*fix(fixer) {
const readonlyInsertionTarget =
esNode.type === AST_NODE_TYPES.PropertyDefinition &&
esNode.computed
? nullThrows(
context.sourceCode.getTokenBefore(nameNode),
'Expected to find a token before computed property name',
)
: nameNode;
yield fixer.insertTextBefore(
readonlyInsertionTarget,
'readonly ',
);
if (typeAnnotation) {
yield fixer.insertTextAfter(nameNode, `: ${typeAnnotation}`);
}
},
});
}
},
[functionScopeBoundaries](
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| TSESTree.MethodDefinition,
): void {
if (ASTUtils.isConstructor(node)) {
classScopeStack[classScopeStack.length - 1].enterConstructor(
services.esTreeNodeToTSNodeMap.get(node),
);
} else if (isFunctionScopeBoundaryInStack(node)) {
classScopeStack[classScopeStack.length - 1].enterNonConstructor();
}
},
MemberExpression(node): void {
if (classScopeStack.length === 0) {
return;
}
const classScope = classScopeStack[classScopeStack.length - 1];
if (!node.computed) {
const tsNode = services.esTreeNodeToTSNodeMap.get(
node,
) as ts.PropertyAccessExpression;
handlePropertyAccessExpression(tsNode, tsNode.parent, classScope);
} else {
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
if (
ts.isElementAccessExpression(tsNode) &&
ts.isBinaryExpression(tsNode.parent) &&
tsNode.parent.left === tsNode &&
tsutils.isAssignmentKind(tsNode.parent.operatorToken.kind)
) {
const memberName = getStaticMemberAccessValue(node, context);
if (typeof memberName === 'string') {
classScope.addVariableModificationByName(
tsNode.expression,
memberName,
);
}
}
}
},
};
}
ClassScope.addDeclaredVariable(node: ParameterOrPropertyDeclaration): void¶
Parameters:
nodeParameterOrPropertyDeclaration
Returns: void
Calls:
tsutils.isModifierFlagSetts.isArrowFunctiongetMemberName(tsutils.isModifierFlagSet(node, ts.ModifierFlags.Static) ? this.privateModifiableStatics : this.privateModifiableMembers ).set
Code
public addDeclaredVariable(node: ParameterOrPropertyDeclaration): void {
if (
!(
tsutils.isModifierFlagSet(node, ts.ModifierFlags.Private) ||
node.name.kind === ts.SyntaxKind.PrivateIdentifier
) ||
tsutils.isModifierFlagSet(
node,
ts.ModifierFlags.Accessor | ts.ModifierFlags.Readonly,
)
) {
return;
}
if (
this.onlyInlineLambdas &&
node.initializer != null &&
!ts.isArrowFunction(node.initializer)
) {
return;
}
const memberName = getMemberName(node.name);
if (memberName == null) {
return;
}
(tsutils.isModifierFlagSet(node, ts.ModifierFlags.Static)
? this.privateModifiableStatics
: this.privateModifiableMembers
).set(memberName, node);
}
ClassScope.addVariableModification(node: ts.PropertyAccessExpression): void¶
Parameters:
nodets.PropertyAccessExpression
Returns: void
Calls:
this.addVariableModificationByName
Code
ClassScope.addVariableModificationByName(expression: ts.Expression, memberName: string): void¶
Parameters:
expressionts.ExpressionmemberNamestring
Returns: void
Calls:
this.checker.getTypeAtLocationthis.getTypeToClassRelationthis.memberVariableWithConstructorModifications.addthis.memberVariableModifications.addthis.staticVariableModifications.add
Code
public addVariableModificationByName(
expression: ts.Expression,
memberName: string,
): void {
const modifierType = this.checker.getTypeAtLocation(expression);
const relationOfModifierTypeToClass =
this.getTypeToClassRelation(modifierType);
if (
relationOfModifierTypeToClass === TypeToClassRelation.Instance &&
this.constructorScopeDepth === DIRECTLY_INSIDE_CONSTRUCTOR
) {
this.memberVariableWithConstructorModifications.add(memberName);
return;
}
if (
relationOfModifierTypeToClass === TypeToClassRelation.Instance ||
relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
) {
this.memberVariableModifications.add(memberName);
}
if (
relationOfModifierTypeToClass === TypeToClassRelation.Class ||
relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
) {
this.staticVariableModifications.add(memberName);
}
}
ClassScope.enterConstructor(node: | ts.ConstructorDeclaration | ts.GetAccβ¦): void¶
Parameters:
node| ts.ConstructorDeclaration | ts.GetAccessorDeclaration | ts.MethodDeclaration | ts.SetAccessorDeclaration
Returns: void
Calls:
tsutils.isModifierFlagSetthis.addDeclaredVariable
Code
public enterConstructor(
node:
| ts.ConstructorDeclaration
| ts.GetAccessorDeclaration
| ts.MethodDeclaration
| ts.SetAccessorDeclaration,
): void {
this.constructorScopeDepth = DIRECTLY_INSIDE_CONSTRUCTOR;
for (const parameter of node.parameters) {
if (tsutils.isModifierFlagSet(parameter, ts.ModifierFlags.Private)) {
this.addDeclaredVariable(parameter);
}
}
}
ClassScope.enterNonConstructor(): void¶
Returns: void
Code
ClassScope.exitConstructor(): void¶
Returns: void
ClassScope.exitNonConstructor(): void¶
Returns: void
Code
ClassScope.finalizeUnmodifiedPrivateNonReadonlys(): ParameterOrPropertyDeclaration[]¶
Returns: ParameterOrPropertyDeclaration[]
Calls:
this.memberVariableModifications.forEachthis.privateModifiableMembers.deletethis.staticVariableModifications.forEachthis.privateModifiableStatics.deletethis.privateModifiableMembers.valuesthis.privateModifiableStatics.values
Code
public finalizeUnmodifiedPrivateNonReadonlys(): ParameterOrPropertyDeclaration[] {
this.memberVariableModifications.forEach(variableName => {
this.privateModifiableMembers.delete(variableName);
});
this.staticVariableModifications.forEach(variableName => {
this.privateModifiableStatics.delete(variableName);
});
return [
...this.privateModifiableMembers.values(),
...this.privateModifiableStatics.values(),
];
}
ClassScope.getTypeToClassRelation(type: ts.Type): TypeToClassRelation¶
Parameters:
typets.Type
Returns: TypeToClassRelation
Calls:
type.isIntersectionthis.getTypeToClassRelationtype.isUniontype.getSymboltypeIsOrHasBaseType (from ../util)tsutils.isObjectTypetsutils.isObjectFlagSet
Internal Comments:
// any union of class/instance and something else will prevent access to
// private members, so we assume that union consists only of classes
// or class instances, because otherwise tsc will report an error
Code
public getTypeToClassRelation(type: ts.Type): TypeToClassRelation {
if (type.isIntersection()) {
let result: TypeToClassRelation = TypeToClassRelation.None;
for (const subType of type.types) {
const subTypeResult = this.getTypeToClassRelation(subType);
switch (subTypeResult) {
case TypeToClassRelation.Class:
if (result === TypeToClassRelation.Instance) {
return TypeToClassRelation.ClassAndInstance;
}
result = TypeToClassRelation.Class;
break;
case TypeToClassRelation.Instance:
if (result === TypeToClassRelation.Class) {
return TypeToClassRelation.ClassAndInstance;
}
result = TypeToClassRelation.Instance;
break;
}
}
return result;
}
if (type.isUnion()) {
// any union of class/instance and something else will prevent access to
// private members, so we assume that union consists only of classes
// or class instances, because otherwise tsc will report an error
return this.getTypeToClassRelation(type.types[0]);
}
if (!type.getSymbol() || !typeIsOrHasBaseType(type, this.classType)) {
return TypeToClassRelation.None;
}
const typeIsClass =
tsutils.isObjectType(type) &&
tsutils.isObjectFlagSet(type, ts.ObjectFlags.Anonymous);
if (typeIsClass) {
return TypeToClassRelation.Class;
}
return TypeToClassRelation.Instance;
}
ClassScope.memberHasConstructorModifications(name: string): boolean¶
Parameters:
namestring
Returns: boolean
Calls:
this.memberVariableWithConstructorModifications.has
Code
getMemberName(name: ts.DeclarationName): string | undefined¶
Parameters:
namets.DeclarationName
Returns: string | undefined
Calls:
ts.isIdentifierts.isPrivateIdentifierts.isStringLiteralts.isNoSubstitutionTemplateLiteralts.isNumericLiteralts.isComputedPropertyNamets.isPropertyAccessExpressionexpression.getText
Code
function getMemberName(name: ts.DeclarationName): string | undefined {
if (
ts.isIdentifier(name) ||
ts.isPrivateIdentifier(name) ||
ts.isStringLiteral(name) ||
ts.isNoSubstitutionTemplateLiteral(name) ||
ts.isNumericLiteral(name)
) {
return name.text;
}
if (ts.isComputedPropertyName(name)) {
const expression = name.expression;
if (ts.isNumericLiteral(expression)) {
return expression.text;
}
if (
ts.isPropertyAccessExpression(expression) &&
ts.isIdentifier(expression.expression) &&
expression.expression.text === 'Symbol'
) {
return expression.getText();
}
}
return undefined;
}
Internal helpers¶
Declared inside another function in this file.
handlePropertyAccessExpression(node: ts.PropertyAccessExpression, parent: ts.Node, classScope: ClassScope): void¶
Parameters:
nodets.PropertyAccessExpressionparentts.NodeclassScopeClassScope
Returns: void
Calls:
ts.isBinaryExpressionhandleParentBinaryExpressionts.isDeleteExpressionisDestructuringAssignmentclassScope.addVariableModificationts.isPostfixUnaryExpressionts.isPrefixUnaryExpressionhandleParentPostfixOrPrefixUnaryExpression
Code
function handlePropertyAccessExpression(
node: ts.PropertyAccessExpression,
parent: ts.Node,
classScope: ClassScope,
): void {
if (ts.isBinaryExpression(parent)) {
handleParentBinaryExpression(node, parent, classScope);
return;
}
if (ts.isDeleteExpression(parent) || isDestructuringAssignment(node)) {
classScope.addVariableModification(node);
return;
}
if (
ts.isPostfixUnaryExpression(parent) ||
ts.isPrefixUnaryExpression(parent)
) {
handleParentPostfixOrPrefixUnaryExpression(parent, classScope);
}
}
handleParentBinaryExpression(node: ts.PropertyAccessExpression, parent: ts.BinaryExpression, classScope: ClassScope): void¶
Parameters:
nodets.PropertyAccessExpressionparentts.BinaryExpressionclassScopeClassScope
Returns: void
Calls:
tsutils.isAssignmentKindclassScope.addVariableModification
Code
handleParentPostfixOrPrefixUnaryExpression(node: ts.PostfixUnaryExpression | ts.PrefixUnβ¦, classScope: ClassScope): void¶
Parameters:
nodets.PostfixUnaryExpression | ts.PrefixUnaryExpressionclassScopeClassScope
Returns: void
Calls:
classScope.addVariableModification
Code
function handleParentPostfixOrPrefixUnaryExpression(
node: ts.PostfixUnaryExpression | ts.PrefixUnaryExpression,
classScope: ClassScope,
): void {
if (
node.operator === ts.SyntaxKind.PlusPlusToken ||
node.operator === ts.SyntaxKind.MinusMinusToken
) {
classScope.addVariableModification(
node.operand as ts.PropertyAccessExpression,
);
}
}
isDestructuringAssignment(node: ts.PropertyAccessExpression): boolean¶
Parameters:
nodets.PropertyAccessExpression
Returns: boolean
Calls:
ts.isObjectLiteralExpressionts.isArrayLiteralExpressionts.isSpreadAssignmentts.isSpreadElementts.isBinaryExpressionts.isPropertyAccessExpression
Code
function isDestructuringAssignment(
node: ts.PropertyAccessExpression,
): boolean {
let current = node.parent as ts.Node | undefined;
while (current) {
const parent = current.parent;
if (
ts.isObjectLiteralExpression(parent) ||
ts.isArrayLiteralExpression(parent) ||
ts.isSpreadAssignment(parent) ||
(ts.isSpreadElement(parent) &&
ts.isArrayLiteralExpression(parent.parent))
) {
current = parent;
} else if (
ts.isBinaryExpression(parent) &&
!ts.isPropertyAccessExpression(current)
) {
return (
parent.left === current &&
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken
);
} else {
break;
}
}
return false;
}
isFunctionScopeBoundaryInStack(node: | TSESTree.ArrowFunctionExpression | TSβ¦): boolean¶
Parameters:
node| TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.MethodDefinition
Returns: boolean
Calls:
services.esTreeNodeToTSNodeMap.getts.isConstructorDeclarationtsutils.isFunctionScopeBoundary
Code
function isFunctionScopeBoundaryInStack(
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| TSESTree.MethodDefinition,
): boolean {
if (classScopeStack.length === 0) {
return false;
}
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
if (ts.isConstructorDeclaration(tsNode)) {
return false;
}
return tsutils.isFunctionScopeBoundary(tsNode);
}
getEsNodesFromViolatingNode(violatingNode: ParameterOrPropertyDeclaration): { esNode: TSESTree.Node; nameNode: TSESTree.Node }¶
Parameters:
violatingNodeParameterOrPropertyDeclaration
Returns: { esNode: TSESTree.Node; nameNode: TSESTree.Node }
Calls:
services.tsNodeToESTreeNodeMap.get
Code
getTypeAnnotationForViolatingNode(node: TSESTree.Node, type: ts.Type, initializerType: ts.Type): any¶
Parameters:
nodeTSESTree.Nodetypets.TypeinitializerTypets.Type
Returns: any
Calls:
checker.typeToStringtsutils.isTypeFlagSetcontext.sourceCode.getScopeASTUtils.findVariablevariable.defs.findservices.getTypeAtLocation
Internal Comments:
Code
function getTypeAnnotationForViolatingNode(
node: TSESTree.Node,
type: ts.Type,
initializerType: ts.Type,
) {
const annotation = checker.typeToString(type);
// verify the about-to-be-added type annotation is in-scope
if (tsutils.isTypeFlagSet(initializerType, ts.TypeFlags.EnumLiteral)) {
const scope = context.sourceCode.getScope(node);
const variable = ASTUtils.findVariable(scope, annotation);
if (variable == null) {
return null;
}
const definition = variable.defs.find(def => def.isTypeDefinition);
if (definition == null) {
return null;
}
const definitionType = services.getTypeAtLocation(definition.node);
if (definitionType !== type) {
return null;
}
}
return annotation;
}
Classes¶
ClassScope¶
Methods (10) β full entries under Functions
| Method | Signature |
|---|---|
addDeclaredVariable |
(node: ParameterOrPropertyDeclaration): void |
addVariableModification |
(node: ts.PropertyAccessExpression): void |
addVariableModificationByName |
(expression: ts.Expression, memberName: string): void |
enterConstructor |
(node: \| ts.ConstructorDeclaration \| ts.GetAccessorDeclaration \| ts.MethodDeclaration \| ts.Se... |
enterNonConstructor |
(): void |
exitConstructor |
(): void |
exitNonConstructor |
(): void |
finalizeUnmodifiedPrivateNonReadonlys |
(): ParameterOrPropertyDeclaration[] |
getTypeToClassRelation |
(type: ts.Type): TypeToClassRelation |
memberHasConstructorModifications |
(name: string): boolean |
Type Aliases¶
MessageIds¶
Options¶
ParameterOrPropertyDeclaration¶
Enums¶
enum TypeToClassRelation¶
Members¶
| Name | Value | Description |
|---|---|---|
ClassAndInstance |
auto | not shown |
Class |
auto | not shown |
Instance |
auto | not shown |
None |
auto | not shown |
Generated by Syntax Scribe