Files
ISA-Frontend/docs/guidelines/code-style.md
Lorenz Hilpert 67dcb49a1d Enhance UI components with new input control directive and styling.
-  **Feature**: Added InputControlDirective for better input handling
- 🎨 **Style**: Updated button and text-field styles for loading states
- 🛠️ **Refactor**: Improved button component structure and disabled state handling
- 📚 **Docs**: Updated code style guidelines with new control flow syntax
2025-04-02 15:16:35 +02:00

5.8 KiB

Code Style Guidelines

General Principles

  • Readability First: Write code that is easy to read and understand.
  • Consistency: Follow the same patterns and conventions throughout the codebase.
  • Clean Code: Avoid unnecessary complexity and keep functions small and focused.

Extended Guidelines for Angular and TypeScript

This section extends the core code style principles with Angular-specific and advanced TypeScript best practices to ensure consistency and maintainability in our projects.

Angular Enhancements

  • Change Detection: Use the OnPush strategy by default for better performance.
  • Lifecycle Hooks: Explicitly implement Angular lifecycle interfaces.
  • Template Management: Keep templates concise and use the async pipe to handle observables.
  • Component Structure: Follow best practices for component modularization to enhance readability and testability.

TypeScript Enhancements

  • Strict Type Checking: Enable strict mode (strict: true) and avoid excessive use of any.
  • Interfaces vs. Types: Prefer interfaces for object definitions and use type aliases for unions and intersections.
  • Generics: Use meaningful type parameter names and constrain generics when applicable.
  • Documentation: Employ JSDoc comments functions and generic parameters to improve code clarity.

TypeScript Guidelines

  • Strict Typing:

    • Enable strict: true in tsconfig.json
    • Avoid any unless absolutely necessary
    • Use unknown instead of any when type is truly unknown
    • Always specify return types for functions
    • Use type inference for variable declarations where types are obvious
    // Good
    const user: User = getUserById('123');
    const items = ['apple', 'banana']; // Type inference is fine here
    
    // Bad
    const user: any = getUserById('123');
    const items: string[] = ['apple', 'banana']; // Unnecessary type annotation
    
  • Interfaces and Types:

    • Prefer interface over type for object definitions
    • Use type for unions, intersections, and mapped types
    • Follow Angular's naming convention: IComponentProps for props interfaces
    • Extend interfaces instead of repeating properties
    • Use readonly modifiers where appropriate
    // Good
    interface IBaseProps {
      readonly id: string;
      name: string;
    }
    
    interface IUserProps extends IBaseProps {
      email: string;
    }
    
    type ValidationResult = 'success' | 'error' | 'pending';
    
    // Bad
    type UserProps = {
      id: string;
      name: string;
      email: string;
    };
    
  • Enums and Constants:

    • Use const enum for better performance
    • Only use regular enum when runtime access is required
    • Prefer union types for simple string literals
  • Functions and Methods:

    • Use arrow functions for callbacks and class methods
    • Explicitly type parameters and return values
    • Keep functions pure when possible
    • Use function overloads for complex type scenarios
    • Document complex functions with JSDoc
    // Good
    /**
     * Fetches a user by ID and transforms it to the required format
     * @param id - The user's unique identifier
     * @param includeDetails - Whether to include additional user details
     */
    const getUser = (id: string, includeDetails = false): Promise<IUser> => {
      // ...implementation
    };
    
    // Bad
    function getUser(id) {
      // ...implementation
    }
    
  • Generics:

    • Use meaningful type parameter names (e.g., T for type, K for key)
    • Constrain generic types when possible using extends
    • Document generic parameters using JSDoc

Example:

// Good
interface IUserProps {
  id: string;
  name: string;
}

interface IAdminProps extends IUserProps {
  permissions: string[];
}

const enum UserRole {
  Admin = 'ADMIN',
  User = 'USER',
}

const getUser = <T extends IUserProps>(id: string): Promise<T> => {
  // ...implementation
};

// Bad
type User = {
  id: any;
  name: any;
};

function getUser(id) {
  // ...implementation
}

Angular-Specific Guidelines

  • Components:

    • Use OnPush change detection strategy by default
    • Implement lifecycle hooks interfaces explicitly
    • Keep templates small and focused
    • Use async pipe instead of manual subscription management
    // Good
    @Component({
      selector: 'app-user-list',
      changeDetection: ChangeDetectionStrategy.OnPush,
    })
    export class UserListComponent implements OnInit, OnDestroy {
      users$ = this.userService.getUsers().pipe(shareReplay(1));
    }
    
    // Bad
    @Component({
      selector: 'app-user-list',
    })
    export class UserListComponent {
      users: User[] = [];
      subscription: Subscription;
    
      ngOnInit() {
        this.subscription = this.userService.getUsers().subscribe((users) => (this.users = users));
      }
    }
    
  • Templates

    • Use new control flow syntax - instead if *ngIf use the @if syntax

Project-Specific Preferences

  • Frameworks: Follow best practices for Nx, Hono, and Zod.
  • Testing: Use Jest with Spectator for unit tests and follow the Arrange-Act-Assert pattern.
  • File Naming: Use kebab-case for filenames (e.g., my-component.ts).
  • Comments: Use JSDoc for documenting functions, classes, and modules.

Formatting

  • Indentation: Use 2 spaces for indentation.
  • Line Length: Limit lines to 80 characters where possible.
  • Semicolons: Always use semicolons.
  • Quotes: Use single quotes for strings, except when using template literals.

Linting and Tools

  • Use ESLint with the recommended TypeScript and Nx configurations.
  • Prettier should be used for consistent formatting.

Example

// Good Example
interface User {
  id: string;
  name: string;
}

const getUser = (id: string): User => {
  // ...function logic...
};

// Bad Example
function getUser(id) {
  // ...function logic...
}

References