TypeScript Class Design Standards
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 (✅)
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 (✅)
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 (✅)
const MAX_MESSAGES = 100;
let declaration (❌)
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 (✅)
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 (❌)
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 (✅)
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.
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 (✅)
function getTotalCost(firstCost: number, secondCost: number, thirdCost?: number);
Overloaded parameter example (❌)
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.
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.
class Payment {
bill: IBill;
constructor(bill: IBill) {
this.bill = bill;
}
generateReceipt(): Receipt {
// Class specific logic
}
}