Skip to content

Commit 63355f9

Browse files
feat: add educational OOP concepts following TypeScript Handbook (#290)
1 parent 19b4ced commit 63355f9

5 files changed

Lines changed: 186 additions & 0 deletions

File tree

oop/Abstraction.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @concept Abstraction
3+
*
4+
* Abstraction is a fundamental Object-Oriented Programming concept that hides
5+
* complex implementation details and exposes only the necessary features
6+
* of an object. It allows you to focus on what an object does instead of
7+
* how it does it.
8+
*
9+
* @see {@link https://en.wikipedia.org/wiki/Abstraction_(computer_science) | Wikipedia: Abstraction}
10+
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#abstract-classes-and-members | TypeScript Docs: Abstract Classes}
11+
*/
12+
export abstract class MailService {
13+
/**
14+
* Abstract method definition.
15+
* Subclasses must implement this method to provide specific mail delivery logic.
16+
*/
17+
abstract send(): void;
18+
19+
/**
20+
* Shared internal logic available to all subclasses,
21+
* abstracting away the connection process.
22+
*/
23+
protected connect(): void {
24+
console.log("Connecting to mail server...");
25+
}
26+
}
27+
28+
/**
29+
* Concrete implementation of the abstract MailService.
30+
*/
31+
export class GmailService extends MailService {
32+
send(): void {
33+
this.connect();
34+
console.log("Sending mail via Gmail...");
35+
}
36+
}

oop/Encapsulation.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @concept Encapsulation
3+
*
4+
* Encapsulation is the bundling of data (properties) and the methods
5+
* that operate on that data within a single unit or class. It restricts
6+
* direct external access to the object's internal state, promoting
7+
* data integrity and security.
8+
*
9+
* @see {@link https://en.wikipedia.org/wiki/Encapsulation_(computer_programming) | Wikipedia: Encapsulation}
10+
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#member-visibility | TypeScript Docs: Member Visibility}
11+
*/
12+
export class SmartWatch {
13+
/**
14+
* Private internal state, inaccessible directly from outside the class.
15+
*/
16+
private _stepCount: number = 0;
17+
18+
/**
19+
* Controlled public interface to safely modify the internal state.
20+
* @param steps - The number of steps to add.
21+
*/
22+
public addSteps(steps: number): void {
23+
if (steps > 0) this._stepCount += steps;
24+
}
25+
26+
/**
27+
* Public getter to safely retrieve the internal state without allowing direct modification.
28+
* @returns The current step count.
29+
*/
30+
public get steps(): number {
31+
return this._stepCount;
32+
}
33+
}

oop/Inheritance.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @concept Inheritance
3+
*
4+
* Inheritance is a mechanism where a new class (subclass) acquires the
5+
* properties and methods of an existing class (superclass). It promotes
6+
* code reusability and establishes a hierarchical relationship between classes.
7+
*
8+
* @see {@link https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming) | Wikipedia: Inheritance}
9+
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#extends-clauses | TypeScript Docs: Extends Clauses}
10+
*/
11+
export class User {
12+
constructor(public name: string) { }
13+
14+
/**
15+
* Common method available to all Users and their subclasses.
16+
*/
17+
login(): void {
18+
console.log(`${this.name} logged in.`);
19+
}
20+
}
21+
22+
/**
23+
* Admin inherits the properties (name) and methods (login) from User,
24+
* while defining its own specialized behaviors.
25+
*/
26+
export class Admin extends User {
27+
/**
28+
* Specialized method only available to Admins.
29+
*/
30+
deleteUser(userName: string): void {
31+
console.log(`Admin ${this.name} is deleting user: ${userName}`);
32+
}
33+
}

oop/Polymorphism.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @concept Polymorphism
3+
*
4+
* Polymorphism (meaning "many forms") allows objects of different classes
5+
* to be treated as objects of a common superclass or interface. It enables
6+
* a single interface to represent different underlying forms (data types).
7+
*
8+
* @see {@link https://en.wikipedia.org/wiki/Polymorphism_(computer_science) | Wikipedia: Polymorphism}
9+
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#implements-clauses | TypeScript Docs: Implements Clauses}
10+
*/
11+
export interface UIElement {
12+
/**
13+
* Common interface method that all implementing classes must define.
14+
*/
15+
render(): void;
16+
}
17+
18+
/**
19+
* Specific implementation of UIElement behaving as a TextBox.
20+
*/
21+
export class TextBox implements UIElement {
22+
render(): void {
23+
console.log("Rendering a TextBox");
24+
}
25+
}
26+
27+
/**
28+
* Specific implementation of UIElement behaving as a Checkbox.
29+
*/
30+
export class Checkbox implements UIElement {
31+
render(): void {
32+
console.log("Rendering a Checkbox");
33+
}
34+
}

oop/test/OOP.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { MailService, GmailService } from '../Abstraction';
2+
import { SmartWatch } from '../Encapsulation';
3+
import { User, Admin } from '../Inheritance';
4+
import { UIElement, TextBox, Checkbox } from '../Polymorphism';
5+
6+
describe('TypeScript OOP Educational Examples', () => {
7+
test('Abstraction: Should correctly use MailService and GmailService', () => {
8+
const gmail = new GmailService();
9+
const spy = jest.spyOn(console, 'log');
10+
11+
gmail.send();
12+
13+
expect(spy).toHaveBeenCalledWith("Connecting to mail server...");
14+
expect(spy).toHaveBeenCalledWith("Sending mail via Gmail...");
15+
spy.mockRestore();
16+
});
17+
18+
test('Encapsulation: Should protect internal step count', () => {
19+
const watch = new SmartWatch();
20+
watch.addSteps(100);
21+
expect(watch.steps).toBe(100);
22+
23+
watch.addSteps(-50); // should not reduce steps because step must be > 0
24+
expect(watch.steps).toBe(100);
25+
// watch._stepCount is inaccessible here (TypeScript error)
26+
});
27+
28+
test('Inheritance: Should allow Admin to inherit User and have custom behavior', () => {
29+
const admin = new Admin("Alice");
30+
const spy = jest.spyOn(console, 'log');
31+
32+
admin.login();
33+
admin.deleteUser("Bob");
34+
35+
expect(spy).toHaveBeenCalledWith("Alice logged in.");
36+
expect(spy).toHaveBeenCalledWith("Admin Alice is deleting user: Bob");
37+
spy.mockRestore();
38+
});
39+
40+
test('Polymorphism: Should work with different UIElement types', () => {
41+
const elements: UIElement[] = [new TextBox(), new Checkbox()];
42+
const spy = jest.spyOn(console, 'log');
43+
44+
elements.forEach(el => el.render());
45+
46+
expect(spy).toHaveBeenCalledWith("Rendering a TextBox");
47+
expect(spy).toHaveBeenCalledWith("Rendering a Checkbox");
48+
spy.mockRestore();
49+
});
50+
});

0 commit comments

Comments
 (0)