# TypeScript Class Design Standards · TS-01

## Standard Metadata

- Area: TypeScript
- Owner: Frontend Steering Group
- Last reviewed: 2026-09-09
- Next review due from: 2026-12-01
- Enforcement: ESLint / TypeScript compiler / code review
- Status: Active

## Types

Use TypeScript's static typing feature to write clean code, catching type-related errors at compile-time rather than at runtime.

### Use type annotations for function parameters and return types · TS-01.1 · **MUST**

Ensure type safety by always using type annotations for function parameters and return types.

#### Parameter and return type annotation example (✅)

```ts
function AccumulateCosts(costs: number[]): number {
  return costs.reduce((partialSum, nextValue) => partialSum + nextValue, 0);
}
```

### Use type annotations variables of complex objects · TS-01.2 · **MUST**

Ensure type safety by always using type annotations on variables, when the type of such variables is complex and hard to discern.

#### Explicit typing example (✅)

```ts
let receipt: IReceipt = { 
  customerName: input.customerName,
  vatAmount: input.vatAmount,
  grossCost: input.netCost + input.vatAmount,
  serviceFee: (input.netCost * (100 / config.serviceFeePercentage)) 
};
```

### Use `const` over `let` for variables that do not change · TS-01.3 · **MUST**

Declare immutability of variables that will not be reassigned after initialisation. Additionally, preventing accidental reassignment.

#### `const` declaration (✅)

```ts
const MAX_MESSAGES = 100;
```

#### `let` declaration (❌)

```ts
let MAX_MESSAGES = 100;
```

### Use `unknown` over `any` · TS-01.4 · **MUST**

Enforce safe type checking before performing potentially unsafe operations by using `unknown` instead of `any`.

#### `unknown` example (✅)

```ts
function handleAnonymousRequest(request: unknown): string {
  // Code won't compile due to errors.
  const requestText = request.toUpperCase();

  // Good practice, checking the 
  if (typeof input === 'string') {
    return input.toUpperCase();
  }
}
```

#### `any` example (❌)

```ts
function handleAnonymousRequest(request: any): string {
  // Bad practice, compile-time safe but can cause runtime errors.
  return request.toUpperCase();
}
```

### Use `readonly` for properties that won't change after initialisation · TS-01.5 · **SHOULD**

Utilise `readonly` when a class property won't change after it has been instantiated as an object.

#### `readonly` example (✅)

```ts
class User {
  readonly id: number;

  constructor(id: number) {
    this.id = id;
  }
}
```

### Use `undefined` over `null` · TS-01.6 · **MUST**

Typescript, by default, represents missing values as `undefined` and advises that null is avoided in the [official docs](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined).

### Use optional parameters · TS-01.7 · **SHOULD**

When writing several overloads that differ only in trailing parameters, prefer the use of optional parameters.  

#### Optional parameter example (✅)

```ts
function getTotalCost(firstCost: number, secondCost: number, thirdCost?: number);
```

#### Overloaded parameter example (❌)

```ts
function getTotalCost(firstCost: number, secondCost: number);
function getTotalCost(firstCost: number, secondCost: number, thirdCost: number);
```

### Avoid redundant type annotations when TypeScript can infer types · TS-01.8 · **COULD**

Explicitly typing everything in a codebase should be decided by the project team. Unecessary verbosity may lead to inconcise code and incurs overhead on development time.

## Object-Oriented Programming

Leverage TypeScript's OOP features to model applications around encapsulation, abstraction, inheritance and polymorphism.

### Use interfaces to define object structures · TS-01.9 · **MUST**

Use interfaces to define the shape of objects, enforcing adherence to structures which model business requirements.

```ts
interface Receipt {
  customerName: string;
  vatAmount: number;
  grossCost: number;
  serviceFee: number | null;
  groupBooking: boolean;
}
```

### Use classes to define objects · TS-01.10 · **MUST**

Use classes to define objects with properties and methods.

```ts
class Payment {
  bill: IBill;

  constructor(bill: IBill) {
    this.bill = bill;
  }

  generateReceipt(): Receipt {
    // Class specific logic
  }
}
```
