TypeScript Classes
TypeScript adds types and visibility modifiers to JavaScript classes.
This page assumes you already know how plain JavaScript classes work.
Members: Types
The members of a class — its properties and methods — get types with type annotations, just like variables.
Example
name: string declares a property that must hold text.
Person with a name property typed string, create an instance and set its name to "Jane". The example prints nothing.
Output: TypeScript file and captured output
class Person {
name: string;
}
const person = new Person();
person.name = "Jane";
Members: Visibility
Class members can also carry special modifiers that control where they can be accessed from.
Note: There are three main visibility modifiers in TypeScript.
public— (default) the member can be accessed from anywhereprivate— the member can only be accessed from inside the classprotected— the member can be accessed from the class itself and any class that inherits from it (see the inheritance sections below)
Note: Inside a class, the this keyword usually refers to the current instance of the class.
Example
Calling getName() is fine because the method is public. Reading person.name from outside the class is a compile error because name is private.
private and public visibility modifiers on class members.
Declare a class Person with a private name: string, a public constructor that sets it, and a public getName() method; create a Person named "Jane", log getName() (allowed), then log person.name directly so the compiler reports that name is private and only accessible within the class.
Output: TypeScript file and captured output
class Person {
private name: string;
public constructor(name: string) {
this.name = name;
}
public getName(): string {
return this.name;
}
}
const person = new Person("Jane");
console.log(person.getName()); // no error
console.log(person.name); // Error: Property 'name' is private and only accessible within class 'Person'.
Parameter Properties
TypeScript offers a shortcut for defining class members in the constructor: add a visibility modifier to the constructor parameter, and it becomes a member automatically.
Example
private name: string in the constructor both declares the name member and assigns it — no separate property line needed.
Person whose constructor takes private name: string (with an empty body), plus a public getName() method returning it; create a Person named "Jane" and log getName(), printing Jane.
Output: TypeScript file and captured output
class Person {
// name is a private member variable
public constructor(private name: string) {}
public getName(): string {
return this.name;
}
}
const person = new Person("Jane");
console.log(person.getName());
Readonly
Like with arrays, the readonly keyword stops a class member from being changed after it is set.
Example
A readonly member must be given its value either where it is declared or in the constructor, and cannot change afterwards.
readonly modifier on a class property.
Declare a class Person with a private readonly name: string set once in the constructor (with a comment explaining it cannot change afterwards) and a public getName() method; create a Person named "Jane" and log getName(), printing Jane.
Output: TypeScript file and captured output
class Person {
private readonly name: string;
public constructor(name: string) {
// name cannot be changed after this initial definition, which has to be either at its declaration or in the constructor.
this.name = name;
}
public getName(): string {
return this.name;
}
}
const person = new Person("Jane");
console.log(person.getName());
Inheritance: Implements
An interface can describe the shape a class must follow. The class promises to match it with the implements keyword.
Note: A class can implement several interfaces by listing them after implements, separated by commas, like class Rectangle implements Shape, Colored {
Example
Rectangle must provide a getArea method because the Shape interface requires one.
implements keyword.
Declare an interface Shape with a getArea function property returning number, then a class Rectangle that implements it using protected readonly width and height parameter properties and a getArea() method returning width times height. The example only declares and prints nothing.
Output: TypeScript file and captured output
interface Shape {
getArea: () => number;
}
class Rectangle implements Shape {
public constructor(protected readonly width: number, protected readonly height: number) {}
public getArea(): number {
return this.width * this.height;
}
}
Inheritance: Extends
A class can build on another class with the extends keyword.
A class can only extend one other class.
Example
Square extends Rectangle, passes the same value for width and height to super, and inherits getArea without writing it again.
extends keyword.
Declare a Shape interface and a Rectangle class implementing it (protected readonly width and height, getArea() returning their product), then a Square class extending Rectangle whose constructor takes one width and calls super(width, width), inheriting getArea. The example only declares and prints nothing.
Output: TypeScript file and captured output
interface Shape {
getArea: () => number;
}
class Rectangle implements Shape {
public constructor(protected readonly width: number, protected readonly height: number) {}
public getArea(): number {
return this.width * this.height;
}
}
class Square extends Rectangle {
public constructor(width: number) {
super(width, width);
}
// getArea gets inherited from Rectangle
}
Override
When a class extends another, it can replace members of the parent that have the same name.
Newer versions of TypeScript let you mark this explicitly with the override keyword.
Note: By default the override keyword is optional when overriding a method; it only helps catch mistakes, such as "overriding" a method that does not exist in the parent. Turn on the noImplicitOverride setting to require it.
Example
Square replaces the toString it inherited from Rectangle. The protected members are still accessible from Square.
override keyword.
Declare a Shape interface, a Rectangle class implementing it with protected readonly width and height, getArea() and a toString() returning "Rectangle[width=..., height=...]", then a Square subclass whose override toString() returns "Square[width=...]". The example only declares and prints nothing.
Output: TypeScript file and captured output
interface Shape {
getArea: () => number;
}
class Rectangle implements Shape {
// using protected for these members allows access from classes that extend from this class, such as Square
public constructor(protected readonly width: number, protected readonly height: number) {}
public getArea(): number {
return this.width * this.height;
}
public toString(): string {
return `Rectangle[width=${this.width}, height=${this.height}]`;
}
}
class Square extends Rectangle {
public constructor(width: number) {
super(width, width);
}
// this toString replaces the toString from Rectangle
public override toString(): string {
return `Square[width=${this.width}]`;
}
}
Abstract Classes
A class can be written so it only serves as a base for other classes, without implementing all of its members.
This is done with the abstract keyword on the class.
Members left without an implementation also use the abstract keyword.
Note: An abstract class cannot be instantiated directly, because not all of its members are implemented.
Example
Polygon leaves getArea abstract but implements toString using it. Rectangle extends Polygon and supplies the missing getArea.
abstract class Polygon with a public abstract getArea(): number and a concrete toString() returning "Polygon[area=...]", then a Rectangle class extending it with protected readonly width and height, calling super() and implementing getArea(). The example only declares and prints nothing.
Output: TypeScript file and captured output
abstract class Polygon {
public abstract getArea(): number;
public toString(): string {
return `Polygon[area=${this.getArea()}]`;
}
}
class Rectangle extends Polygon {
public constructor(protected readonly width: number, protected readonly height: number) {
super();
}
public getArea(): number {
return this.width * this.height;
}
}