projectrules.ai

TypeScript Coding Conventions

TypescriptCoding StandardsBest PracticesSoftware DevelopmentProgramming

Description

Typescript coding conventions

Globs

*.ts,*.tsx
---
description: Typescript coding conventions
globs: *.ts,*.tsx
---

# TypeScript Coding Conventions

## Imports

- When importing in this project, use relative imports.
- When importing the package ably, do `import * as Ably from 'ably'`

## Exports

- Export interfaces and types that are part of the public API, make sure you add them to the relevant index.ts: [index.ts](mdc:src/core/index.ts) or [index.ts](mdc:src/react/index.ts)
- Use named exports over default exports
- Group related exports together

```typescript
export interface Messages extends EmitsDiscontinuities {
  // Interface members
}
```

## Interface and Type Declarations

### Interface Naming
- Use PascalCase for interface names
- Use descriptive names that reflect the purpose
- Prefix interfaces with 'I' is not required
```typescript
interface MessageEventPayload {
  type: MessageEvents;
  message: Message;
}
```

### Interface Documentation
- Use JSDoc style comments for interfaces
- Include a clear description of the interface's purpose
- Document all properties inline, alongside the property itself.
- Document all errors using `@throws`.
- Document the return type using `@return`
- Link using {@link} to types mentioned.
```typescript
/**
 * Options for querying messages in a chat room.
 */
interface QueryOptions {
  /**
   * The start of the time window to query from.
   * @defaultValue The beginning of time
   */
  start?: number;
}
```

## Enums

### Enum Declarations
- Use PascalCase for enum names
- Use PascalCase for enum members
- Include JSDoc documentation for both enum and members
```typescript
export enum OrderBy {
  /**
   * Return results in ascending order (oldest first).
   */
  OldestFirst = 'oldestFirst',

  /**
   * Return results in descending order (newest first).
   */
  NewestFirst = 'newestFirst',
}
```

## Classes

### Class Structure
- Use PascalCase for class names
- Implement interfaces explicitly
- Avoid extension wherever possible.
- Document class purpose and implementation details.

```typescript
/**
 * @inheritDoc
 */
export class DefaultMessages
  extends EventEmitter
  implements Messages, HandlesDiscontinuity {
  // Class implementation
}
```

### Class Members
- Use underscore prefix for private members
- Group members by visibility (private, protected, public)
- Document all public methods and properties

```typescript
private readonly _roomId: string;
private readonly _channel: Ably.RealtimeChannel;
```

## Methods

### Method Documentation
- Use JSDoc for all public methods
- Make use of `@param`, `@returns` and `@throws`
```typescript
/**
 * Subscribe to new messages in this chat room.
 * @param listener callback that will be called
 * @returns A response object that allows you to control the subscription.
 */
subscribe(listener: MessageListener): MessageSubscriptionResponse;
```

### Method Parameters
- Use descriptive parameter names
- Group related parameters into parameter objects, rather than having many parameters.
- Use optional parameters with default values when appropriate

```typescript
async update(
  message: Message,
  update: UpdateMessageParams,
  details?: OperationDetails
): Promise
```

## Error Handling

### Error Types
- The error type used is `ErrorInfo` from the package 'ably' - here [ably.d.ts](mdc:node_modules/ably/ably.d.ts)
  - The first argument is the message, this should be descriptive.
  - The second argument is the error code.
  - The third argument is the status code. When the error code is in the 10000-59999 range, the status code will be the HTTP status code that matches the first 3 numbers.
- Specific error codes can be found in the `ErrorCodes` enum in the [errors.ts](mdc:src/core/errors.ts) file.

```typescript
throw new Ably.ErrorInfo(
  'cannot query history; listener has not been subscribed yet',
  40000,
  400,
) as unknown as Error;
```

## Logging

### Logging Conventions
- Use consistent logging levels (trace, debug, error)
- Most method calls (for key classes like messages, presence, room status) will have a trace level log at the top. Don't worry about this for data classes etc.
- Include relevant context in log messages, the second argument is a free-formed object for any relevant context.
- Never log instances of Ably channels, this doesn't work.
- Structure log messages for easy parsing.

```typescript
this._logger.trace('Messages.subscribe();');
this._logger.debug('Messages.update(); message update successfully', { updatedMessage });
```

## Type Safety

### Type Assertions

- Minimize use of type assertions
- Use type guards when possible
- Document when and why type assertions are necessary
- Never use `any`. If we really can't avoid it, use `unknown`, but strong typing always preferred.

```typescript
const channelWithProperties = this._channel as Ably.RealtimeChannel & {
  properties: {
    attachSerial: string | undefined;
    channelSerial: string | undefined;
  };
};
```

## Promises and Async

### Async/Await
- Use async/await over raw promises
- Handle promise rejections appropriately
- Document asynchronous behavior

```typescript
async send(params: SendMessageParams): Promise {
  this._logger.trace('Messages.send();', { params });
  const response = await this._chatApi.sendMessage(this._roomId, params);
  return new DefaultMessage(/* ... */);
}
```

## Feature Specification

Refer to [feature-specification.mdc](mdc:.cursor/rules/feature-specification.mdc) for information on how to handle feature specification points in code.
TypeScript Coding Conventions