Table of Contents
- 1.1 Setting up the demo application
- 1.2 Understanding classes
- 1.3 Create and instantiate classes
- 1.4 Class methods
- 1.5 The constructor
- 1.6 Overloading of constructors
- 1.7 Parameter Properties
- 2.1 Understanding inheritance
- 2.2 TypeScript inheritance
- 2.3 Use super()
- 2.4 Overriding of methods
- 2.5 Access Modifiers
- 2.6 Static Class Members
- 2.7 Interfaces
- 2.8 Abstract Classes
1. Working with Classes
1.1 Setting up the demo application
Project context
This training uses the fictitious brand GloboTicket, a company that sells event tickets. The demo application allows you to explore the concepts of object-oriented programming (OOP) in TypeScript in a concrete and realistic context.
Execution environment
Several options exist for running TypeScript. The choice chosen in this training is Node.js with the npm package manager. TypeScript is installed in the project if it is not globally available on the system.
The tsx package
A key package is used: tsx. This package compiles the TypeScript and executes it directly, eliminating the need for a separate compilation step. The basic command is:
npx tsx index.ts
The main file will always be index.ts.
Project configuration (package.json)
{
"devDependencies": {
"tsx": "^4.20.3",
"typescript": "^5.8.3"
}
}
Starting structure
01-setup/event.class.ts — The starting empty class:
export class Event {
}
01-setup/index.ts — The initial entry point:
import { Event } from './event.class';
console.log('Glockticket is ready.');
This minimalist code confirms that the environment is working. The import of the Event class is already in place, and a simple console.log produces the initial output.
1.2 Understanding Classes
What is a class?
Classes are the fundamental pillars of object-oriented programming. A class is a blueprint (construction plan) that defines the structure and behavior of objects.
Contents of a class
A class can contain:
-
Properties: Data specific to each instance. For example, for the
Eventclass: -
date— the date of the event -
title(orname) — the name or title of the event -
venue— information about the venue -
Methods: functions attached to the class that perform actions. For example:
-
cancel()— cancel the event -
reschedule()— reschedule the event
Classes and instances
The class definition is the blueprint. From this blueprint, we create instances via the instantiation process. Each instance is independent:
- One instance can represent event A, another an event B.
- The two instances share the same defined properties (e.g.,
title,date) but not the values of these properties. - All instances have the same methods defined in the class.
1.3 Create and instantiate classes
Syntax for defining a class
We use the keyword class followed by the name of the class. TypeScript enriches the standard ECMAScript syntax.
03-class/event.class.ts:
export class Event {
date: Date;
title: string;
}
Here, two properties are defined: date of type Date and title of type string. We prefix the class with export to make it importable from other files.
Instantiation and usage
03-class/index.ts:
import { Event } from './event.class';
const event = new Event();
event.title = 'Digital Cowboys';
console.log(event.title);
- The
newkeyword creates an instance of theEventclass. - The
.syntax (dot syntax) allows you to access and modify properties. event.title = 'Digital Cowboys'assigns the value to the property.- Console output confirms that the value has been assigned.
Important points
- The class resides in its own
event.class.tsfile, which is a good code organization practice. - TypeScript (and Visual Studio Code) provides autocompletion of properties using static typing.
1.4 Class methods
Role of methods
properties (class members) are used to contain data specific to an instance. methods are there for functionality — what the class knows how to do.
Defining a method
04-method/event.class.ts:
export class Event {
title: string;
setTitle(t: string): void {
this.title = t;
}
}
Details:
setTitleis a method that expects an argument of typestring.- Its return type is
void— it returns nothing. thisrefers to the current instance of the class.this.title = tassigns thetitleproperty of the current instance with the argument received.
Method usage
04-method/index.ts:
import { Event } from './event.class';
const event = new Event();
event.setTitle('Container Enthusiasm');
console.log(event.title);
Instead of directly accessing the title property, we use the setTitle method. TypeScript and modern IDEs offer autocompletion on available methods. The console output confirms that the method has set the property.
1.5 The constructor
What is a constructor?
The constructor is a special type of method in a class. It is automatically called when instantiating the class with new. It is this which allows the properties to be initialized upon creation of the object.
Syntax
05-constructor/event.class.ts:
export class Event {
title: string;
constructor(t: string) {
this.title = t;
}
}
Key points:
- The name is fixed:
constructor. - It has no return type — it always returns the instance of the class, which is why you cannot (and should not) specify one for it.
- During
new Event('Digital Cowboys'), parentheses allow arguments to be passed to the constructor.
Usage
05-constructor/index.ts:
import { Event } from './event.class';
const event = new Event('Digital Cowboys');
console.log(event.title);
new Event('Digital Cowboys') calls the constructor with t = 'Digital Cowboys', which assigns this.title = 'Digital Cowboys'. The constructor here replaces the role of setTitle while being more concise and initializing the object immediately.
1.6 Overloading constructors
Why overload a constructor?
In certain scenarios, you may want to instantiate a class in different ways depending on the available data. Constructor overloading allows you to have several signatures for the same constructor.
Principle
We can define a constructor which accepts:
- Only a
title(when the date is not yet known) - A
dateand atitle(when both are available)
Syntax of overloading
06-overload/event.class.ts:
export class Event {
title: string;
date: Date;
constructor(t: string);
constructor(d: Date, t: string);
constructor(td: string | Date, t: string = '') {
this.title = (typeof(td) === 'string') ? td : t;
this.date = (td instanceof Date) ? td : new Date();
}
}
Analysis:
- The first two lines are the overload signatures. They define the accepted forms.
- The third line is the actual implementation, which should cover all cases.
- The first
tdparameter is of typestring | Date(union type). - If
tdis astring, this is the title. Otherwise, the title is int. - If
tdis an instance ofDate, this is the date. Otherwise, we usenew Date().
Usage
06-overload/index.ts:
import { Event } from './event.class';
let events:Event[] = [];
events.push(new Event('Digital Cowboys'));
events.push(new Event(new Date(2027, 5, 13), 'Container Enthusiasm'));
events.forEach(event =>
console.log(`${event.title} @ ${event.date.toDateString()}`));
- The first
Eventonly has a title — the date is initialized tonew Date()(today). - The second
Eventhas a specific date (June 13, 2027) and a title. - Both forms are valid and TypeScript compiles without errors.
1.7 Parameter Properties
The problem
When you have a class with properties to initialize in a constructor, the code can become repetitive: you declare the property, then you assign it in the constructor.
The solution: Parameter Properties
TypeScript offers a shortcut syntax called parameter properties that merges the property declaration and assignment in the constructor into a single line.
07-parameterproperty/event.class.ts:
export class Event {
constructor(public title: string) {
}
}
By adding an access modifier (like public) in front of the constructor parameter, TypeScript:
- Automatically creates a
titleproperty on the class. - Automatically assigns the parameter value to this property.
- The constructor body remains empty — everything is handled implicitly.
Usage
07-parameterproperty/index.ts:
import { Event } from './event.class';
const event = new Event('Network Firewall Squad');
console.log(event.title);
Property instantiation and access works exactly as before, but with much more concise class code. The next module explores access modifiers (public, private, protected) in detail.
2. Inheritance and Abstraction
2.1 Understanding inheritance
Beyond simple classes
Module 1 covered how to configure classes with properties and methods. inheritance (inheritance) allows you to go further: a class can be derived from an existing class, inheriting its properties and methods while adding its own.
A concrete example: GloboTicket
In the context of the GloboTicket platform, we think about event types from the perspective of a ticket buyer.
The Event class (generic) contains:
date— event datevenue— location of the eventcancel()— method to cancel a ticket (flexible or insurance ticket)
But a concert is a specific type of event with an additional property: artist. A concert also has a different business rule for cancellation (concert tickets are often non-refundable).
The class hierarchy
Event (classe de base)
├── Concert (classe dérivée — ajoute: artist)
└── Tour (classe dérivée — ajoute: reschedule())
When Concert inherits from Event:
Concertautomatically inheritsdate,venueandcancel().Concertadds its ownartistproperty.Concertcan overridecancel()with its own logic.
Likewise for Tour:
Tourinheritsdate,venueandcancel().Touradds its ownreschedule()method.
Inheritance avoids code duplication: common properties are defined only once in the base class.
2.2 Inheritance in TypeScript
The keyword extends
In TypeScript, we use the extends keyword to create a derived class (child) from a base class (parent).
02-inheritance/event.class.ts:
export class Event {
date: Date;
venue: string;
}
02-inheritance/concert.class.ts:
import { Event } from './event.class';
export class Concert extends Event {
artist: string;
}
The Concert class:
- Inherits from
dateandvenue(defined inEvent) - Add own
artistproperty - Result:
Concerthas three properties —date,venue,artist
Usage
02-inheritance/index.ts:
import { Concert } from './concert.class';
const concert = new Concert();
concert.artist = 'Network Firewall Squad';
concert.venue = 'Olympic Stadium';
console.log(concert.artist, '@', concert.venue);
Both artist (added property) and venue (inherited property) can be accessed via the same instance. The console output displays both values.
2.3 Using super()
The concept of superclass
When Concert is derived from Event, Event is called the superclass or base class. In the class hierarchy, she is “above”.
Why call super()?
If the base class has a constructor, the derived class must be able to call it to ensure that initialization of the base class is done. This is the role of super().
03-super/event.class.ts:
export class Event {
date: Date;
venue: string;
constructor() {
console.log('Event created.');
}
}
03-super/concert.class.ts:
import { Event } from './event.class';
export class Concert extends Event {
artist: string;
constructor(artist: string) {
super();
this.artist = artist;
}
}
TypeScript rule: super() must be called before any access to this in the constructor of a derived class. If we try to access this.artist before super(), TypeScript throws an error: “super must be called before accessing this in the constructor of a derived class”.
Visual Studio Code also immediately reports if super() is missing in the constructor of a derived class.
Usage
03-super/index.ts:
import { Concert } from './concert.class';
const concert = new Concert('Digital Cowboys');
concert.venue = 'Pluralsight Arena';
console.log(concert.artist, '@', concert.venue);
During new Concert('Digital Cowboys'):
- The
Concertconstructor is called. super()calls theEventconstructor → displays “Event created.”this.artist = artistaffects the artist.
2.4 Method overriding
Why overload a method?
By default, a derived class inherits the implementation of methods from the base class. But sometimes we want a different implementation in the derived class. This is method overriding.
04-overriding/event.class.ts:
export class Event {
date: Date;
title: string;
venue: string;
toDisplayName(): string {
return this.title;
}
}
The toDisplayName() method simply returns the title of the event — useful for displaying on printed tickets or the website.
Overloading in derived class
04-overriding/concert.class.ts:
import { Event } from './event.class';
export class Concert extends Event {
artist: string;
toDisplayName(): string {
if (this.artist === '' ||
this.artist === undefined) {
return super.toDisplayName();
} else {
return this.artist + ' @ ' +
this.venue;
}
}
}
- The
toDisplayName()method is redefined inConcert. - If
artistis absent (multi-artist concert, for example), we delegate to the implementation of the base class viasuper.toDisplayName(). This avoids code duplication. - Otherwise, we return an enriched representation:
"Artist @ Location". superhere refers to the base class in the context of the current instance.
Usage
04-overriding/index.ts:
import { Concert } from './concert.class';
const concert = new Concert();
concert.title = 'Pluralsight Summit';
//concert.artist = 'Digital Cowboys';
concert.venue = 'Pluralsight Arena';
console.log(concert.toDisplayName());
Here, artist is commented out — so toDisplayName() of Concert delegates to super.toDisplayName() and simply returns 'Pluralsight Summit'. By uncommenting the artist line, the result would be "Digital Cowboys @ Pluralsight Arena".
2.5 Access Modifiers
Why control access?
access modifiers allow you to control who can access a property or method. This is a fundamental principle of encapsulation in OOP.
public — universal access
public is the default modifier. A public property or method is accessible by anyone, from anywhere. It can be written explicitly to make the code more readable.
private — restricted access to the class
A private property or method is only accessible from within the same class. Derived classes and external code do not have access to it.
protected — the happy medium
protected offers an intermediate level. A protected property or method is accessible:
- In the class that defines it.
- In all classes that inherit from it (derived classes).
It is not accessible from the outside (class consumer code).
readonly
The readonly modifier (often combined with public or private) prevents reassignment after initialization.
05-accessmodifiers/event.class.ts:
export class Event {
//public readonly date: Date;
private date: Date;
protected title: string;
protected venue: string;
constructor() {
this.date = new Date();
}
}
dateisprivate: only code inEventcan access it. Derived classes and external code do not see this property.titleandvenueareprotected: accessible inEventand inConcert(which inherits fromEvent), but not fromindex.ts.
05-accessmodifiers/concert.class.ts:
import { Event } from './event.class';
export class Concert extends Event {
private artist: string;
constructor(artist: string, venue: string) {
super();
this.artist = artist;
this.venue = venue;
}
}
artistisprivateinConcert.venueis accessible because it isprotectedinEvent(accessible in derived classes).
05-accessmodifiers/index.ts:
import { Concert } from './concert.class';
const concert = new Concert(
'Network Firewall Squad',
'Pluralsight HQ');
From index.ts, we cannot access concert.date (private) nor concert.title or concert.venue (protected). TypeScript throws an error if you try.
Summary table
| Modifier | Same class | Derived classes | Exterior |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
2.6 Static Class Members
Static properties and methods
A static member (property or method) of a class is accessible independently of a specific instance. It is shared by the class itself, not by its instances.
Example with an enum
06-static/eventtype.enum.ts:
export enum EventType {
Concert,
Tour
}
06-static/event.class.ts:
export class Event {
constructor() {
console.log('Event created.');
}
}
06-static/concert.class.ts:
import { Event } from './event.class';
import { EventType } from './eventtype.enum';
export class Concert extends Event {
static readonly type: EventType = EventType.Concert;
}
typeisstatic: it does not belong to an instance, but to the class itself.readonly: value cannot be changed after initialization — a concert will always be a concert.
Access via class name
06-static/index.ts:
import { Concert } from './concert.class';
import { EventType } from './eventtype.enum';
let concert = new Concert;
//console.log(EventType[concert.type]);
console.log(EventType[Concert.type]);
We cannot access a static member via an instance (concert.type generates an error). We must use the class name: Concert.type. TypeScript also reports: “Did you mean to access the static member ‘Concert.type’ instead?”
Parentheses in new Concert are optional in the absence of arguments, but this is a matter of style.
2.7 Interfaces
The role of interfaces
Until now, base classes had content (properties, method implementations). Sometimes, we want to define only the structure — the blueprint — without any implementation. This is the role of interfaces.
Define an interface
07-interfaces/ievent.class.ts:
export interface IEvent {
cancel(): boolean;
}
- Naming convention: the
Iprefix indicates that it is an interface (optional but recommended). - The
IEventinterface declares that acancel()method returning abooleanmust be implemented. There is no implementation.
Implement an interface
We use the keyword implements (and not extends) to link a class to an interface.
07-interfaces/concert.class.ts:
import { IEvent } from './ievent.class';
export class Concert implements IEvent {
cancel(): boolean {
console.error('Concert tickets are non-refundable!');
return false;
}
}
Concert implements IEventrequires thatConcertprovides an implementation ofcancel().- If
cancel()is absent, TypeScript throws an error: “Class ‘Concert’ incorrectly implements interface ‘IEvent’. Property ‘cancel’ is missing in type ‘Concert’ but required in type ‘IEvent’”.
Usage
07-interfaces/index.ts:
import { Concert } from './concert.class';
let concert = new Concert;
concert.cancel();
Interfaces vs base classes
| Appearance | Interface | Base class |
|---|---|---|
| Implementation | ❌ Banned | ✅ Possible |
| Keyword | implements | extends |
| Direct instantiation | ❌ | ✅ |
| Multiple inheritance | ✅ (a class can implement multiple interfaces) | ❌ (only one base class) |
2.8 Abstract Classes
The best of both worlds?
Interfaces cannot have an implementation. Ordinary base classes always have an implementation. abstract classes offer something in between: they can have methods with an implementation and abstract methods (without implementation) that derived classes must implement.
Define an abstract class
08-abstract/event.class.ts:
export abstract class Event {
abstract cancel(): boolean;
constructor(protected title: string) {}
protected toDisplayName(): string {
return this.title;
}
}
- The
abstractkeyword beforeclassdeclares the abstract class. abstract cancel(): boolean;declares an abstract method: any derived class must implement it.- The constructor is concrete (with property
protected titleparameter). toDisplayName()is a concreteprotectedmethod: accessible to derived classes, with its implementation provided.- An abstract class cannot be instantiated directly (
new Event()is prohibited).
Implement an abstract class
08-abstract/concert.class.ts:
import { Event } from './event.class';
export class Concert extends Event {
cancel(): boolean {
const displayName: string = this.toDisplayName();
console.error(`Concert tickets for "${displayName}" are non-refundable!`);
return false;
}
}
Concert extends Event— we useextends, notimplements, because it is still a class.cancel()is implemented (required — otherwise TypeScript throws an error).- Method
cancel()callsthis.toDisplayName()— accessible because it isprotectedin the abstract class.
Usage
08-abstract/index.ts:
import { Concert } from './concert.class';
let concert = new Concert('Network Firewall Squad');
concert.cancel();
Output: Concert tickets for "Network Firewall Squad" are non-refundable!
Abstract class vs Interface
| Appearance | Abstract Class | Interface |
|---|---|---|
| Keyword derivation | extends | implements |
| Concrete implementations | ✅ Possible | ❌ Banned |
| Methods without implementation | ✅ (abstract) | ✅ (all) |
| Direct instantiation | ❌ | ❌ |
| Multiple inheritance | ❌ | ✅ |
3. Summary of key concepts
The pillars of OOP in TypeScript
| Concept | Keyword | Description |
|---|---|---|
| Class | class | Blueprint for creating objects with properties and methods |
| Instantiation | new | Create an instance of a class |
| Legacy | extends | Derive one class from another |
| Interface | interface / implements | Define a contract without implementation |
| Abstract class | abstract class | Non-instantiable class with concrete and abstract methods |
| Superclass | super() | Call the constructor or methods of the base class |
| Method overloading | (redefinition) | Override inherited implementation in derived class |
Access Modifiers
| Modifier | Accessibility |
|---|---|
public | Everywhere (default) |
protected | Class + derived classes |
private | Class only |
readonly | Read only after initialization |
static | Via class name, without instance |
Specific TypeScript syntaxes
// Parameter property — déclare ET initialise en une ligne
constructor(public title: string) {}
// Constructor overloading
constructor(t: string);
constructor(d: Date, t: string);
constructor(td: string | Date, t: string = '') { /* impl */ }
// Abstract class
abstract class Event {
abstract cancel(): boolean; // méthode à implémenter
toDisplayName(): string { return this.title; } // méthode concrète
}
// Interface
interface IEvent {
cancel(): boolean;
}
// Enum
enum EventType {
Concert,
Tour
}
// Static readonly
static readonly type: EventType = EventType.Concert;
GloboTicket demo hierarchy
Event (classe de base / abstract class)
├── Propriétés : date, title, venue
├── Méthodes : toDisplayName(), cancel() (abstract)
│
├── Concert (extends Event, implements IEvent)
│ ├── Propriétés héritées : date, title, venue
│ ├── Propriétés ajoutées : artist
│ ├── static readonly type: EventType.Concert
│ └── cancel() : "Concert tickets are non-refundable!"
│
└── Tour (extends Event)
├── Propriétés héritées : date, venue
└── Méthodes ajoutées : reschedule()
Evolution flow of the Event class
| Step | Demo | Addition |
|---|---|---|
| Empty class | 01-setup | export class Event {} |
| Properties | 03-class | date: Date, title: string |
| Method | 04-method | setTitle(t: string): void |
| Constructor | 05-constructor | constructor(t: string) |
| Overload | 06-overload | Two constructor signatures |
| Parameter property | 07-parameterproperty | constructor(public title: string) |
| Legacy | 02-inheritance | Concert extends Event |
| super() | 03-super | Call to base constructor |
| Overriding | 04-overriding | Redefining toDisplayName() |
| Access modifiers | 05-accessmodifiers | private, protected |
| Static | 06-static | static readonly type + enum |
| Interface | 07-interfaces | Concert implements IEvent |
| Abstract | 08-abstract | abstract class Event |
Search Terms
typescript · foundations · object-oriented · programming · react · frontend · development · class · usage · classes · access · abstract · method · constructor · inheritance · interface · interfaces · methods · overloading · properties · syntax · define · defining · globoticket