All files readonlyArrayRule.ts

100% Statements 38/38
92.86% Branches 26/28
100% Functions 6/6
100% Lines 36/36
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 771x 1x 1x   1x 1x 13x   13x     1x     950x 950x 950x         950x   79x 15x   64x 64x 7x 7x 57x 57x 57x 57x     64x 64x   871x         950x     86x 86x 8x   78x 34x 16x 16x 16x                         16x         926x      
import * as ts from "typescript";
import * as Lint from "tslint";
import * as Shared from "./readonly-shared";
 
export class Rule extends Lint.Rules.AbstractRule {
  public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
    return this.applyWithFunction(
      sourceFile,
      (ctx: Lint.WalkContext<Shared.Options>) => Shared.walk(ctx, checkNode, "Only ReadonlyArray allowed."),
      Shared.parseOptions(this.ruleArguments));
  }
}
 
function checkNode(node: ts.Node, ctx: Lint.WalkContext<Shared.Options>): ReadonlyArray<Shared.InvalidNode> {
  const explicitTypeFailures = checkArrayTypeOrReference(node, ctx);
  const implicitTypeFailures = checkVariableOrParameterImplicitType(node, ctx);
  return explicitTypeFailures.concat(implicitTypeFailures);
}
 
function checkArrayTypeOrReference(node: ts.Node, ctx: Lint.WalkContext<Shared.Options>): ReadonlyArray<Shared.InvalidNode> {
  // We need to check both shorthand syntax "number[]" and type reference "Array<number>"
  if (node.kind === ts.SyntaxKind.ArrayType
    || (node.kind === ts.SyntaxKind.TypeReference && (node as ts.TypeReferenceNode).typeName.getText(ctx.sourceFile) === "Array")) {
    if (node.parent && Shared.shouldIgnorePrefix(node.parent, ctx.options, ctx.sourceFile)) {
      return [];
    }
    let typeArgument: string = "T";
    if (node.kind === ts.SyntaxKind.ArrayType) {
      const typeNode = node as ts.ArrayTypeNode;
      typeArgument = typeNode.elementType.getFullText(ctx.sourceFile).trim();
    } else Eif (node.kind === ts.SyntaxKind.TypeReference) {
      const typeNode = node as ts.TypeReferenceNode;
      Eif (typeNode.typeArguments) {
        typeArgument = typeNode.typeArguments[0].getFullText(ctx.sourceFile).trim();
      }
    }
    const length = node.getWidth(ctx.sourceFile);
    return [Shared.createInvalidNode(node, new Lint.Replacement(node.end - length, length, `ReadonlyArray<${typeArgument}>`))];
  }
  return [];
}
 
function checkVariableOrParameterImplicitType(node: ts.Node, ctx: Lint.WalkContext<Shared.Options>): ReadonlyArray<Shared.InvalidNode> {
 
  if (node.kind === ts.SyntaxKind.VariableDeclaration || node.kind === ts.SyntaxKind.Parameter
    || node.kind === ts.SyntaxKind.PropertyDeclaration) {
    // The initializer is used to set and implicit type
    const varOrParamNode = node as ts.VariableDeclaration | ts.ParameterDeclaration;
    if (Shared.shouldIgnorePrefix(node, ctx.options, ctx.sourceFile)) {
      return [];
    }
    if (!varOrParamNode.type) {
      if (varOrParamNode.initializer && varOrParamNode.initializer.kind === ts.SyntaxKind.ArrayLiteralExpression) {
        const length = varOrParamNode.name.getWidth(ctx.sourceFile);
        const nameText = varOrParamNode.name.getText(ctx.sourceFile);
        let typeArgument = "any";
        // Not sure it is a good idea to guess what the element types are...
        // const arrayLiteralNode = varOrParamNode.initializer as ts.ArrayLiteralExpression;
        // if (arrayLiteralNode.elements.length > 0) {
        //   const element = arrayLiteralNode.elements[0];
        //   if (element.kind === ts.SyntaxKind.NumericLiteral) {
        //     typeArgument = "number";
        //   } else if (element.kind === ts.SyntaxKind.StringLiteral) {
        //     typeArgument = "string";
        //   } else if (element.kind === ts.SyntaxKind.TrueKeyword || element.kind === ts.SyntaxKind.FalseKeyword) {
        //     typeArgument = "boolean";
        //   }
        // }
        return [Shared.createInvalidNode(varOrParamNode.name,
          new Lint.Replacement(varOrParamNode.name.end - length, length, `${nameText}: ReadonlyArray<${typeArgument}>`))];
      }
    }
  }
  return [];
 
}