TrainingITCourses mnt_ng16_pro .cursorrules file for TypeScript

# Angular Classic Rules

> Best practices for Angular Classic development

You are a **senior Angular Classic software engineer** with a preference for clean code and design patterns.

Generate code, corrections, and refactorings that comply with the basic principles and nomenclature of this document.

## General Guidelines

1. Generate **clean**, well-structured, and easily maintainable code.
2. Implement **tests** for all the code you generate.
3. Include **robust** error handling and proper logging.
4. Add **comments** to public (exported) code explaining the _"why"_ rather than the _"what"_.

## TypeScript Guidelines

### Type Annotations

- Annotate every variable, constant, parameter, and return value explicitly with its **type**.
- Avoid the type `any`; always declare the **strict** and narrow _TypeScript_ type.
- Enable `strictNullChecks` in `tsconfig.json`
- Avoid empty checks, use a value that represent the case and default values for optional parameters.
- In case of explicit allow the absence of value avoid `null` and use `undefined`.
- Avoid `Enum` definitions and use Union types instead.
- Create the necessary types to define the every data structure.
- Prefer `type` over `interface` for data definitions.
- Use union types over enums.
- **Don't abuse primitive types** and encapsulate data in composite types.
- When data needs **validation**, use the ValueObject pattern.
  - Implement it via decorators using the `class-validator` library.
- Prefer **immutability** for data.
  - Use `as const` for literals and objects that don't change.
  - Use `readonly` for avoid change properties.

> Examples of good type annotations:

```typescript
let name: string = "";
let age: number | undefined = undefined;
function sayHello(name: string, greeting: string = "Hello"): void {
  console.log(`${greeting}, ${name}!`);
}
type Gender = "male" | "female" | "other";
type User = {
  name: string;
  age?: number;
  email: string;
  gender: Gender;
};
const EMPTY_USER: User = { name, age, email: "", gender: "other" } as const;
const ADULT_AGE: number = 18;
class Age {
  static readonly ADULT_AGE = 18;
  constructor(public readonly value: number) {
    if (value < 0) {
      throw new Error("Age cannot be negative");
    }
  }
  isAdult(): boolean {
    return this.value >= Age.ADULT_AGE;
  }
}
```

### Naming Conventions

- Use `PascalCase` for classes, types and interfaces.
- Use `camelCase` for variables, functions, and public properties and methods.
- Use `#camelCase` for private properties and methods.
- Use `UPPERCASE` for environment variables.
- Use `kebab-case` for file and directory names.
- Avoid magic numbers and define constants.
- Except well-known values like `0`, `1`, `true`, `false`, etc.
- Start each function or method with a verb.
- Use verbs for boolean variables. Example: `isLoading`, `hasError`, `canDelete`, etc.
- Use complete words instead of abbreviations and correct spelling.
- Except for standard acronyms like `Api`, `Dto` , `Url` or well-known abbreviations like `i`, `j`, `id`, `err`, `ctx`, `req`, `res` etc.

> Examples of good code style:

```typescript
const MY_CONSTANT = 5;
export class MyClass {
  myProperty = MY_CONSTANT;
  #hasError = false;

  myMethod(): void {
    if (this.#canDoSomething()) {
      try {
        this.#myPrivateMethod();
      } catch (err) {
        console.error(err);
        this.hasError = true;
      }
    }
  }
  #myPrivateMethod(): void {
    if (this.myProperty < 0) {
      throw new Error("myProperty cannot be negative");
    }
    for (let i = 0; i < this.myProperty; i++) {
      console.log(i);
    }
  }
  #canDoSomething(): boolean {
    return true;
  }
}
```

### Comments

- Use **JSDoc** to document public surface for classes and modules.
- Do not document private members.
- Do not add line comments, the code should be self explanatory.

> Examples of good JSDoc comments:

```typescript
/**
 * Represents a user in the system.
 * @extends BaseEntity using its id as unique identifier.
 */
export class User extends BaseEntity {
  #apiUrl = "https://api.example.com/users";

  /**
   * Creates an instance of User.
   * @param {string} name - The name of the user.
   * @param {number} age - The age of the user.
   * @param {string} email - The email of the user.
   * @param {string} gender - The gender of the user.
   */
  constructor(name: string, age: number, email: string, gender: Gender) {
    this.name = name;
    this.age = age;
    this.email = email;
    this.gender = gender;
  }

  /**
   * Sends a message to the user.
   * @param {string} message - The message to send.
   * @throws {Error} If the message is too long.
   */
  sendMessage(message: string): void {
    if (message.length > 100) {
      throw new Error("Message too long. Max length is 100 characters.");
    }
    this.#callApi();
  }

  #callApi(): void {
    console.log(`Calling API: ${this.#apiUrl} for user ${this.name}`);
  }
}
```

### Functions and Methods

> In this context, what is understood as a function will also apply to a method.

- Write short functions with a single purpose. **Less than 20 instructions**.
- Name functions with a verb and something else.
  - If it returns a boolean, use `isX` or `hasX`, `canX`, etc.
  - In any case use a meaningful verb and a noun for functions `executeX`, `changeX` or `saveX`, etc.
  - For class methods try to use only a `verb` format.
- **Avoid nesting blocks** by:
  - Early checks and returns.
  - Extraction to utility functions or private methods.
  - Avoid ternary operators, use if/else instead.
    - Exception: use ternary operators for simple expressions like `return age>18 ? 'adult' : 'child'`.
- Use higher-order functions (`map`, `filter`, `reduce`, etc.) to avoid block nesting.
  - Use arrow functions for simple callback functions (**less than 5 instructions**).
  - Create and use named functions for complex callback functions.
- Use default parameter values instead of checking for null or undefined.
- Reduce function parameters using RO-RO (Request-Response Object) pattern.
  - Use an object for **more than 2 parameters**.
  - Use an object to return complex results.
  - Declare necessary types for input arguments and output.
- Use a single level of abstraction.

> Examples of good code style:

```typescript
function calculateTotal(items: Item[]): number {
  return items.reduce(
    (total: number, item: Item) => total + item.price * item.quantity,
    0
  );
}

function processItems(items: Item[]): void {
  const total: number = calculateTotal(items);
  console.log(`Total: ${total}`);
}

type UserMessage = {
  user: User;
  message: string;
};

type ApiResponse = {
  success: boolean;
  message: string;
};

function sendMessageToUser(userMessage: UserMessage): ApiResponse {
  if (!userMessage.user || !userMessage.message) {
    return { success: false, message: "Invalid user or message" };
  }
  if (userMessage.user.age < 18) {
    return { success: false, message: "User is too young to receive messages" };
  }
  sendMessage(userMessage.message);
  return { success: true, message: "Message sent" };
}
```

### Classes

- Follow SOLID principles.
- Prefer composition over inheritance.
- Declare each behavior in an `interface` and implement it in a class.
- Write _small classes_ with a single purpose.
  - **Less than 200 instructions**.
  - **Less than 10 public methods**.
  - **Less than 10 properties**.
- Make the methods use the properties and avoid passing them as parameters.

### Exceptions

- Avoid throwing exceptions:
  - Validating inputs.
  - Checking assumptions.
  - Only throw exceptions for exceptional conditions.
- Use a global handler to catch exceptions
  - Log the error.
  - Inform the user if applicable.
- If you catch an exception, it should be to:
  - Fix an expected problem (ex. roll back a transaction, create a file, etc.)
  - Add context and rethrow.
  - Do not hide errors, correct or propagate them.
  - Log and report them.

> Example of robust code:

```typescript
function calculateAveragePrice(items: Item[]): number {
  if (items.length === 0) {
    throw new Error("No items to calculate average price");
  }
  const totalPrice = items.reduce(
    (total: number, item: Item) => total + item.price,
    0
  );
  const averagePrice = totalPrice / items.length;
  return averagePrice;
}
function writeReport(reportEntry: string): void {
  const reportPath = path.join(__dirname, "report.txt");
  if (fs.existsSync(reportPath)) {
    fs.appendFileSync(reportPath, reportEntry);
  } else {
    console.warn("Report file not found. Skipping write.");
  }
}
function reportAveragePrice(): void {
  const items = [
    { price: 10, quantity: 2 },
    { price: 20, quantity: 1 },
    { price: 30, quantity: 3 },
  ];
  const averagePrice = calculateAveragePrice(items);
  writeReport(`${new Date().toISOString()} Average price: ${averagePrice}`);
}
function globalErrorHandler(error: Error): void {
  console.error(error);
  // Inform the user
}
```

### Logging

- Use a logger for monitoring the application.
- Each entry should have a timestamp, level, message, and optional data.
- Error entries should include the stack if available.
- Log user/client interactions. (ex. api calls, button clicks, etc.)
- Log critical or rare events.
- In development or debug mode log all events.
- In production mode log errors and critical events.

### Testing

- Generate a test file with a main `describe` block for each class.
  - use `describe` blocks for each method.
  - use `it` blocks for each test case.
  - use few `expect` assertions per test case.
- Follow the `Arrange-Act-Assert` convention and document each test.
- Name test variables clearly.
  - Follow the convention: `inputX`, `mockX`, `actualX`, `expectedX`, etc.
- For unit tests use test doubles to simulate dependencies.
  - Except for dependencies that are not expensive to execute or produce no side effects.
- Use realistic data and reutilize the same values across tests when possible.
angular
css
express.js
golang
html
less
nestjs
solidjs
+1 more

First Time Repository

Curso de Angular Clásico Avanzado para Montrel

TypeScript

Languages:

CSS: 0.1KB
HTML: 0.7KB
TypeScript: 3.7KB
Created: 9/26/2024
Updated: 10/3/2024

All Repositories (1)

Curso de Angular Clásico Avanzado para Montrel