or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

accessibility-rules.mdangular-framework.mdcomponent-rules.mddecorator-metadata-rules.mddirective-rules.mdindex.mdinput-output-rules.mdlifecycle-rules.mdpipe-rules.mdstyle-architecture-rules.mdtemplate-rules.md
tile.json

pipe-rules.mddocs/

Pipe Rules

Angular pipe validation rules that enforce naming conventions, decoration requirements, and purity best practices for custom pipes.

Capabilities

Pipe Prefix Rule

Enforces naming conventions for Angular pipes with specified prefixes.

/**
 * Enforces naming conventions for Angular pipes
 * Ensures pipes follow consistent naming patterns with required prefixes
 */
export class PipePrefixRule extends Lint.Rules.AbstractRule {
  static readonly metadata: Lint.IRuleMetadata;
  static readonly FAILURE_STRING: string;
  apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}

Usage Examples:

TSLint configuration:

{
  "rules": {
    "pipe-prefix": [true, "app", "custom"]
  }
}

Good pipe names:

@Pipe({
  name: 'appFormatCurrency'  // ✅ Has 'app' prefix
})
export class FormatCurrencyPipe implements PipeTransform {
  transform(value: number): string {
    return `$${value.toFixed(2)}`;
  }
}

@Pipe({
  name: 'customDateFormat'  // ✅ Has 'custom' prefix
})
export class DateFormatPipe implements PipeTransform {
  transform(value: Date, format: string): string {
    // Custom date formatting logic
    return value.toLocaleDateString();
  }
}

Bad pipe names:

@Pipe({
  name: 'formatCurrency'  // ❌ Missing required prefix
})
export class FormatCurrencyPipe implements PipeTransform {
  transform(value: number): string {
    return `$${value.toFixed(2)}`;
  }
}

No Pipe Impure Rule

Prevents creation of impure pipes that can impact performance.

/**
 * Prevents creation of impure pipes
 * Impure pipes execute on every change detection cycle and can impact performance
 */
export class NoPipeImpureRule extends Lint.Rules.AbstractRule {
  static readonly metadata: Lint.IRuleMetadata;
  static readonly FAILURE_STRING: string;
  apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}

Usage Examples:

Bad impure pipe:

@Pipe({
  name: 'appFilter',
  pure: false  // ❌ Impure pipe
})
export class FilterPipe implements PipeTransform {
  transform(items: any[], filter: any): any[] {
    return items.filter(item => /* filtering logic */);
  }
}

Good pure pipe (default):

@Pipe({
  name: 'appFilter'  // ✅ Pure by default
})
export class FilterPipe implements PipeTransform {
  transform(items: any[], filter: any): any[] {
    return items.filter(item => /* filtering logic */);
  }
}

@Pipe({
  name: 'appFormat',
  pure: true  // ✅ Explicitly pure
})
export class FormatPipe implements PipeTransform {
  transform(value: string): string {
    return value.toUpperCase();
  }
}

Use Pipe Decorator Rule

Enforces use of @Pipe decorator on pipe classes.

/**
 * Enforces @Pipe decorator usage on pipe classes
 * Ensures pipes are properly decorated for Angular to recognize them
 */
export class UsePipeDecoratorRule extends Lint.Rules.AbstractRule {
  static readonly metadata: Lint.IRuleMetadata;
  static readonly FAILURE_STRING: string;
  apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}

Usage Examples:

Good pipe with decorator:

@Pipe({  // ✅ Proper @Pipe decorator
  name: 'appCapitalize'
})
export class CapitalizePipe implements PipeTransform {
  transform(value: string): string {
    return value.charAt(0).toUpperCase() + value.slice(1);
  }
}

Bad pipe without decorator:

// ❌ Missing @Pipe decorator
export class CapitalizePipe implements PipeTransform {
  transform(value: string): string {
    return value.charAt(0).toUpperCase() + value.slice(1);
  }
}

Use Pipe Transform Interface Rule

Enforces implementation of PipeTransform interface on pipe classes.

/**
 * Enforces PipeTransform interface implementation on pipe classes
 * Ensures pipes implement the required transform method with proper typing
 */
export class UsePipeTransformInterfaceRule extends Lint.Rules.AbstractRule {
  static readonly metadata: Lint.IRuleMetadata;
  static readonly FAILURE_STRING: string;
  apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}

Usage Examples:

Good pipe with interface:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'appReverse'
})
export class ReversePipe implements PipeTransform {  // ✅ Implements PipeTransform
  transform(value: string): string {
    return value.split('').reverse().join('');
  }
}

Bad pipe without interface:

@Pipe({
  name: 'appReverse'
})
export class ReversePipe {  // ❌ Missing PipeTransform interface
  transform(value: string): string {
    return value.split('').reverse().join('');
  }
}

Pipe Best Practices

Performance Considerations

// ✅ Pure pipe - cached result, better performance
@Pipe({
  name: 'appExpensive',
  pure: true
})
export class ExpensivePipe implements PipeTransform {
  transform(value: any): any {
    // Expensive computation - result will be cached
    return this.performExpensiveOperation(value);
  }
}

// ❌ Impure pipe - runs on every change detection
@Pipe({
  name: 'appExpensive',
  pure: false
})
export class ExpensivePipe implements PipeTransform {
  transform(value: any): any {
    // This runs on every change detection cycle
    return this.performExpensiveOperation(value);
  }
}

Proper Pipe Design

@Pipe({
  name: 'appFormatDate'
})
export class FormatDatePipe implements PipeTransform {
  transform(value: Date | string, format: string = 'short'): string {
    if (!value) return '';
    
    const date = typeof value === 'string' ? new Date(value) : value;
    
    switch (format) {
      case 'short':
        return date.toLocaleDateString();
      case 'long':
        return date.toLocaleDateString('en-US', { 
          year: 'numeric', 
          month: 'long', 
          day: 'numeric' 
        });
      default:
        return date.toString();
    }
  }
}

Template usage:

<p>{{ user.birthDate | appFormatDate }}</p>
<p>{{ user.birthDate | appFormatDate:'long' }}</p>

Rule Configuration

Example Configuration

{
  "rules": {
    "pipe-prefix": [true, "app", "shared"],
    "no-pipe-impure": true,
    "use-pipe-decorator": true,
    "use-pipe-transform-interface": true
  }
}

Common Pipe Patterns

Data Transformation Pipes

@Pipe({ name: 'appCurrency' })
export class CurrencyPipe implements PipeTransform {
  transform(value: number, currency: string = 'USD'): string {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency
    }).format(value);
  }
}

@Pipe({ name: 'appTruncate' })
export class TruncatePipe implements PipeTransform {
  transform(value: string, length: number = 100): string {
    if (!value || value.length <= length) return value;
    return value.substring(0, length) + '...';
  }
}

Filtering Pipes (Consider Performance)

// ⚠️ Consider using component methods instead for filtering
@Pipe({
  name: 'appFilter',
  pure: false  // Required for filtering dynamic data
})
export class FilterPipe implements PipeTransform {
  transform(items: any[], searchTerm: string): any[] {
    if (!items || !searchTerm) return items;
    
    return items.filter(item => 
      item.name.toLowerCase().includes(searchTerm.toLowerCase())
    );
  }
}

Better approach for filtering:

// Component method approach (better performance)
export class UserListComponent {
  users: User[] = [];
  searchTerm: string = '';

  get filteredUsers(): User[] {
    if (!this.searchTerm) return this.users;
    return this.users.filter(user => 
      user.name.toLowerCase().includes(this.searchTerm.toLowerCase())
    );
  }
}