Skip to content

⬅️ Back to Table of Contents

📄 prefer-readonly.ts

📊 Analysis Summary

Metric Count
🔧 Functions 16
🧱 Classes 1
📦 Imports 9
📊 Variables & Constants 8
📑 Type Aliases 3
🎯 Enums 1

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin/src/rules/prefer-readonly.ts

📦 Imports

Name Source
TSESTree @typescript-eslint/utils
AST_NODE_TYPES @typescript-eslint/utils
ASTUtils @typescript-eslint/utils
createRule ../util
getParserServices ../util
nullThrows ../util
typeIsOrHasBaseType ../util
getMemberHeadLoc ../util/getMemberHeadLoc
getParameterPropertyHeadLoc ../util/getMemberHeadLoc

Variables & Constants

Name Type Kind Value Exported
classScopeStack ClassScope[] const []
current any let/var node.parent as ts.Node | undefined
parent any const current.parent
tsNode ts.PropertyAccessExpression const `services.esTreeNodeToTSNodeMap.get(
node,
) as ts.PropertyAccessExpression`
OUTSIDE_CONSTRUCTOR -1 const -1
DIRECTLY_INSIDE_CONSTRUCTOR 0 const 0
result TypeToClassRelation let/var TypeToClassRelation.None
typeIsClass any const `tsutils.isObjectType(type) &&
tsutils.isObjectFlagSet(type, ts.ObjectFlags.Anonymous)`

Functions

handlePropertyAccessExpression(node: ts.PropertyAccessExpression, parent: ts.Node, classScope: ClassScope): void

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);
      }
    }
  • Parameters:
  • node: ts.PropertyAccessExpression
  • parent: ts.Node
  • classScope: ClassScope
  • Return Type: void
  • Calls:
  • ts.isBinaryExpression
  • handleParentBinaryExpression
  • ts.isDeleteExpression
  • isDestructuringAssignment
  • classScope.addVariableModification
  • ts.isPostfixUnaryExpression
  • ts.isPrefixUnaryExpression
  • handleParentPostfixOrPrefixUnaryExpression

handleParentBinaryExpression(node: ts.PropertyAccessExpression, parent: ts.BinaryExpression, classScope: ClassScope): void

Code
function handleParentBinaryExpression(
      node: ts.PropertyAccessExpression,
      parent: ts.BinaryExpression,
      classScope: ClassScope,
    ): void {
      if (
        parent.left === node &&
        tsutils.isAssignmentKind(parent.operatorToken.kind)
      ) {
        classScope.addVariableModification(node);
      }
    }
  • Parameters:
  • node: ts.PropertyAccessExpression
  • parent: ts.BinaryExpression
  • classScope: ClassScope
  • Return Type: void
  • Calls:
  • tsutils.isAssignmentKind
  • classScope.addVariableModification

handleParentPostfixOrPrefixUnaryExpression(node: ts.PostfixUnaryExpression | ts.PrefixUnaryExpression, classScope: ClassScope): void

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,
        );
      }
    }
  • Parameters:
  • node: ts.PostfixUnaryExpression | ts.PrefixUnaryExpression
  • classScope: ClassScope
  • Return Type: void
  • Calls:
  • classScope.addVariableModification

isDestructuringAssignment(node: ts.PropertyAccessExpression): boolean

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;
    }
  • Parameters:
  • node: ts.PropertyAccessExpression
  • Return Type: boolean
  • Calls:
  • ts.isObjectLiteralExpression
  • ts.isArrayLiteralExpression
  • ts.isSpreadAssignment
  • ts.isSpreadElement
  • ts.isBinaryExpression
  • ts.isPropertyAccessExpression

`isFunctionScopeBoundaryInStack(node: | TSESTree.ArrowFunctionExpression

    | TSESTree.FunctionDeclaration
    | TSESTree.FunctionExpression
    | TSESTree.MethodDefinition): boolean`
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);
    }
  • Parameters:
  • node: | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.MethodDefinition
  • Return Type: boolean
  • Calls:
  • services.esTreeNodeToTSNodeMap.get
  • ts.isConstructorDeclaration
  • tsutils.isFunctionScopeBoundary

getEsNodesFromViolatingNode(violatingNode: ParameterOrPropertyDeclaration): { esNode: TSESTree.Node; nameNode: TSESTree.Node }

Code
function getEsNodesFromViolatingNode(
      violatingNode: ParameterOrPropertyDeclaration,
    ): { esNode: TSESTree.Node; nameNode: TSESTree.Node } {
      return {
        esNode: services.tsNodeToESTreeNodeMap.get(violatingNode),
        nameNode: services.tsNodeToESTreeNodeMap.get(violatingNode.name),
      };
    }
  • Parameters:
  • violatingNode: ParameterOrPropertyDeclaration
  • Return Type: { esNode: TSESTree.Node; nameNode: TSESTree.Node }
  • Calls:
  • services.tsNodeToESTreeNodeMap.get

getTypeAnnotationForViolatingNode(node: TSESTree.Node, type: ts.Type, initializerType: ts.Type): any

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;
    }
  • Parameters:
  • node: TSESTree.Node
  • type: ts.Type
  • initializerType: ts.Type
  • Return Type: any
  • Calls:
  • checker.typeToString
  • tsutils.isTypeFlagSet
  • context.sourceCode.getScope
  • ASTUtils.findVariable
  • variable.defs.find
  • services.getTypeAtLocation
  • Internal Comments:
    // verify the about-to-be-added type annotation is in-scope
    

ClassScope.addDeclaredVariable(node: ParameterOrPropertyDeclaration): void

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,
      ) ||
      ts.isComputedPropertyName(node.name)
    ) {
      return;
    }

    if (
      this.onlyInlineLambdas &&
      node.initializer != null &&
      !ts.isArrowFunction(node.initializer)
    ) {
      return;
    }

    (tsutils.isModifierFlagSet(node, ts.ModifierFlags.Static)
      ? this.privateModifiableStatics
      : this.privateModifiableMembers
    ).set(node.name.getText(), node);
  }
  • Parameters:
  • node: ParameterOrPropertyDeclaration
  • Return Type: void
  • Calls:
  • tsutils.isModifierFlagSet
  • ts.isComputedPropertyName
  • ts.isArrowFunction
  • (tsutils.isModifierFlagSet(node, ts.ModifierFlags.Static) ? this.privateModifiableStatics : this.privateModifiableMembers ).set
  • node.name.getText

ClassScope.addVariableModification(node: ts.PropertyAccessExpression): void

Code
public addVariableModification(node: ts.PropertyAccessExpression): void {
    const modifierType = this.checker.getTypeAtLocation(node.expression);

    const relationOfModifierTypeToClass =
      this.getTypeToClassRelation(modifierType);

    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Instance &&
      this.constructorScopeDepth === DIRECTLY_INSIDE_CONSTRUCTOR
    ) {
      this.memberVariableWithConstructorModifications.add(node.name.text);
      return;
    }

    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Instance ||
      relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
    ) {
      this.memberVariableModifications.add(node.name.text);
    }
    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Class ||
      relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
    ) {
      this.staticVariableModifications.add(node.name.text);
    }
  }
  • Parameters:
  • node: ts.PropertyAccessExpression
  • Return Type: void
  • Calls:
  • this.checker.getTypeAtLocation
  • this.getTypeToClassRelation
  • this.memberVariableWithConstructorModifications.add
  • this.memberVariableModifications.add
  • this.staticVariableModifications.add

`ClassScope.enterConstructor(node: | ts.ConstructorDeclaration

  | ts.GetAccessorDeclaration
  | ts.MethodDeclaration
  | ts.SetAccessorDeclaration): void`
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);
      }
    }
  }
  • Parameters:
  • node: | ts.ConstructorDeclaration | ts.GetAccessorDeclaration | ts.MethodDeclaration | ts.SetAccessorDeclaration
  • Return Type: void
  • Calls:
  • tsutils.isModifierFlagSet
  • this.addDeclaredVariable

ClassScope.enterNonConstructor(): void

Code
public enterNonConstructor(): void {
    if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) {
      this.constructorScopeDepth += 1;
    }
  }
  • Return Type: void

ClassScope.exitConstructor(): void

Code
public exitConstructor(): void {
    this.constructorScopeDepth = OUTSIDE_CONSTRUCTOR;
  }
  • Return Type: void

ClassScope.exitNonConstructor(): void

Code
public exitNonConstructor(): void {
    if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) {
      this.constructorScopeDepth -= 1;
    }
  }
  • Return Type: void

ClassScope.finalizeUnmodifiedPrivateNonReadonlys(): ParameterOrPropertyDeclaration[]

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(),
    ];
  }
  • Return Type: ParameterOrPropertyDeclaration[]
  • Calls:
  • this.memberVariableModifications.forEach
  • this.privateModifiableMembers.delete
  • this.staticVariableModifications.forEach
  • this.privateModifiableStatics.delete
  • this.privateModifiableMembers.values
  • this.privateModifiableStatics.values

ClassScope.getTypeToClassRelation(type: ts.Type): TypeToClassRelation

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;
  }
  • Parameters:
  • type: ts.Type
  • Return Type: TypeToClassRelation
  • Calls:
  • type.isIntersection
  • this.getTypeToClassRelation
  • type.isUnion
  • type.getSymbol
  • typeIsOrHasBaseType (from ../util)
  • tsutils.isObjectType
  • tsutils.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
    

ClassScope.memberHasConstructorModifications(name: string): boolean

Code
public memberHasConstructorModifications(name: string) {
    return this.memberVariableWithConstructorModifications.has(name);
  }
  • Parameters:
  • name: string
  • Return Type: boolean
  • Calls:
  • this.memberVariableWithConstructorModifications.has

Classes

ClassScope

Class Code
class ClassScope {
  private readonly classType: ts.Type;
  private constructorScopeDepth = OUTSIDE_CONSTRUCTOR;
  private readonly memberVariableModifications = new Set<string>();
  private readonly memberVariableWithConstructorModifications =
    new Set<string>();
  private readonly privateModifiableMembers = new Map<
    string,
    ParameterOrPropertyDeclaration
  >();

  private readonly privateModifiableStatics = new Map<
    string,
    ParameterOrPropertyDeclaration
  >();

  private readonly staticVariableModifications = new Set<string>();

  public constructor(
    private readonly checker: ts.TypeChecker,
    classNode: ts.ClassLikeDeclaration,
    private readonly onlyInlineLambdas?: boolean,
  ) {
    const classType = checker.getTypeAtLocation(classNode);
    if (tsutils.isIntersectionType(classType)) {
      this.classType = classType.types[0];
    } else {
      this.classType = classType;
    }

    for (const member of classNode.members) {
      if (ts.isPropertyDeclaration(member)) {
        this.addDeclaredVariable(member);
      }
    }
  }

  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,
      ) ||
      ts.isComputedPropertyName(node.name)
    ) {
      return;
    }

    if (
      this.onlyInlineLambdas &&
      node.initializer != null &&
      !ts.isArrowFunction(node.initializer)
    ) {
      return;
    }

    (tsutils.isModifierFlagSet(node, ts.ModifierFlags.Static)
      ? this.privateModifiableStatics
      : this.privateModifiableMembers
    ).set(node.name.getText(), node);
  }

  public addVariableModification(node: ts.PropertyAccessExpression): void {
    const modifierType = this.checker.getTypeAtLocation(node.expression);

    const relationOfModifierTypeToClass =
      this.getTypeToClassRelation(modifierType);

    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Instance &&
      this.constructorScopeDepth === DIRECTLY_INSIDE_CONSTRUCTOR
    ) {
      this.memberVariableWithConstructorModifications.add(node.name.text);
      return;
    }

    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Instance ||
      relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
    ) {
      this.memberVariableModifications.add(node.name.text);
    }
    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Class ||
      relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
    ) {
      this.staticVariableModifications.add(node.name.text);
    }
  }

  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);
      }
    }
  }

  public enterNonConstructor(): void {
    if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) {
      this.constructorScopeDepth += 1;
    }
  }

  public exitConstructor(): void {
    this.constructorScopeDepth = OUTSIDE_CONSTRUCTOR;
  }

  public exitNonConstructor(): void {
    if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) {
      this.constructorScopeDepth -= 1;
    }
  }

  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(),
    ];
  }

  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;
  }

  public memberHasConstructorModifications(name: string) {
    return this.memberVariableWithConstructorModifications.has(name);
  }
}

Methods

addDeclaredVariable(node: ParameterOrPropertyDeclaration): void
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,
      ) ||
      ts.isComputedPropertyName(node.name)
    ) {
      return;
    }

    if (
      this.onlyInlineLambdas &&
      node.initializer != null &&
      !ts.isArrowFunction(node.initializer)
    ) {
      return;
    }

    (tsutils.isModifierFlagSet(node, ts.ModifierFlags.Static)
      ? this.privateModifiableStatics
      : this.privateModifiableMembers
    ).set(node.name.getText(), node);
  }
addVariableModification(node: ts.PropertyAccessExpression): void
Code
public addVariableModification(node: ts.PropertyAccessExpression): void {
    const modifierType = this.checker.getTypeAtLocation(node.expression);

    const relationOfModifierTypeToClass =
      this.getTypeToClassRelation(modifierType);

    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Instance &&
      this.constructorScopeDepth === DIRECTLY_INSIDE_CONSTRUCTOR
    ) {
      this.memberVariableWithConstructorModifications.add(node.name.text);
      return;
    }

    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Instance ||
      relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
    ) {
      this.memberVariableModifications.add(node.name.text);
    }
    if (
      relationOfModifierTypeToClass === TypeToClassRelation.Class ||
      relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance
    ) {
      this.staticVariableModifications.add(node.name.text);
    }
  }
`enterConstructor(node: | ts.ConstructorDeclaration
  | ts.GetAccessorDeclaration
  | ts.MethodDeclaration
  | ts.SetAccessorDeclaration): void`
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);
      }
    }
  }
enterNonConstructor(): void
Code
public enterNonConstructor(): void {
    if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) {
      this.constructorScopeDepth += 1;
    }
  }
exitConstructor(): void
Code
public exitConstructor(): void {
    this.constructorScopeDepth = OUTSIDE_CONSTRUCTOR;
  }
exitNonConstructor(): void
Code
public exitNonConstructor(): void {
    if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) {
      this.constructorScopeDepth -= 1;
    }
  }
finalizeUnmodifiedPrivateNonReadonlys(): ParameterOrPropertyDeclaration[]
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(),
    ];
  }
getTypeToClassRelation(type: ts.Type): TypeToClassRelation
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;
  }
memberHasConstructorModifications(name: string): boolean
Code
public memberHasConstructorModifications(name: string) {
    return this.memberVariableWithConstructorModifications.has(name);
  }

Type Aliases

MessageIds

type MessageIds = 'preferReadonly';

Options

type Options = [
  {
    onlyInlineLambdas?: boolean;
  },
];

ParameterOrPropertyDeclaration

type ParameterOrPropertyDeclaration = | ts.ParameterDeclaration
  | ts.PropertyDeclaration;

Enums

enum TypeToClassRelation

Enum Code
enum TypeToClassRelation {
  ClassAndInstance,
  Class,
  Instance,
  None,
}

Members

Name Value Description
ClassAndInstance auto
Class auto
Instance auto
None auto