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.