How to Type a TypeScript Class: Fields, Constructors, Methods, and Inheritance
Build and extend a typed TypeScript class, check field initialization and method calls, and see what still depends on JavaScript runtime behavior.

A TypeScript class needs two kinds of attention: its fields and methods need useful types, and its constructor needs to leave each required field initialized. The types help the compiler check calls to the class. The constructor is JavaScript behavior that runs when you create an instance.
Declare fields and initialize them
Here is an Account with two explicitly typed fields and one field whose type comes from its initializer. A SavingsAccount adds an interest rate and reuses the account’s methods:
class Account {
owner: string;
balance: number;
currency = "USD";
constructor(owner: string, openingBalance: number) {
this.owner = owner;
this.balance = openingBalance;
}
deposit(amount: number): number {
this.balance += amount;
return this.balance;
}
describe(): string {
return `${this.owner}: ${this.balance} ${this.currency}`;
}
}
class SavingsAccount extends Account {
interestRate: number;
constructor(owner: string, openingBalance: number, interestRate: number) {
super(owner, openingBalance);
this.interestRate = interestRate;
}
addInterest(): number {
return this.deposit(this.balance * this.interestRate);
}
}
const savings = new SavingsAccount("Ari", 100, 0.05);
console.log(savings.deposit(20)); // 120
console.log(savings.addInterest()); // 126
console.log(savings.describe()); // Ari: 126 USD
The owner, balance, and interestRate declarations say what values those fields hold; their constructors assign the values. The currency initializer both supplies a value when an instance is created and lets TypeScript infer the field’s type as string. These are instance fields: the methods read or change them through this, not by referring to an unqualified balance or owner.
Set strictPropertyInitialization to true in the project’s TypeScript compiler settings when you want the checker to catch a declared field that has no initializer and is not definitely assigned in its constructor. With that setting enabled, removing this.balance = openingBalance makes balance an initialization error. TypeScript checks assignment in the constructor itself; moving the assignment into a method called by the constructor does not establish initialization for this check. There is a definite assignment assertion (balance!: number) for initialization done by other means, but it tells the checker to accept your assertion—it does not put a value in the field. For this class, direct constructor assignment is clearer. See the TypeScript Handbook’s discussion of field initialization.
Type calls and returns, not the constructor’s return
deposit(amount: number): number specifies both its input and its returned balance. describe(): string specifies its result. Constructors accept typed parameters too, but do not take a return-type annotation: creating an Account produces an instance of the class.
The call to super(owner, openingBalance) gives the base constructor the values it needs. In a derived constructor, it must happen before any use of this; only then does SavingsAccount assign interestRate. A savings account can use the inherited owner, balance, and currency fields and call the inherited deposit and describe methods. addInterest uses that existing deposit behavior rather than duplicating the balance update. The Handbook’s constructor and inheritance examples cover the same super() ordering rule.
To check the example in a project, enable strictPropertyInitialization, add the code, and run that project’s TypeScript checker. If it accepts the code, you have checked these field assignments and calls under those compiler settings—not every possible account value. Then temporarily add this deliberately invalid call:
savings.deposit("20"); // Type error: deposit expects a number
The checker should reject the string argument. Remove the line afterward. Finally, execute the JavaScript produced by the project’s normal build and compare the three logged values with 120, 126, and Ari: 126 USD. That separate run checks the behavior of this particular example; a successful type check alone does not calculate a balance.
Keep the class and its type distinct
TypeScript builds on JavaScript’s class feature. A class declaration also gives TypeScript a type for its instances, which is why it can check the constructor call and deposit argument. An interface can describe a type contract, but it is not a replacement for the runtime class behavior used here: creating an instance and running its constructors and methods. The Handbook describes the instance type and constructor function as distinct things a class declaration provides.
Nor does amount: number validate an input arriving from outside typed code. If an API response or other runtime value will become a deposit amount, check that value when it enters the application before calling deposit. The argument mismatch above demonstrates a static check, not runtime validation.


