Skip to content

Commit c485a7c

Browse files
committed
Add examples for generics
1 parent eb0fac0 commit c485a7c

File tree

3 files changed

+133
-3
lines changed

3 files changed

+133
-3
lines changed

Classification/9-generics.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Parametric polymorphism with Generics
2+
3+
interface Account {
4+
id: string;
5+
balance: number;
6+
}
7+
8+
interface SavingsAccount extends Account {
9+
kind: 'savings';
10+
rate: number;
11+
}
12+
13+
interface CreditAccount extends Account {
14+
kind: 'credit';
15+
limit: number;
16+
}
17+
18+
type AnyAccount = SavingsAccount | CreditAccount;
19+
20+
type TransactionKind = 'deposit' | 'withdraw';
21+
22+
interface Transaction<T extends Account> {
23+
kind: TransactionKind;
24+
amount: number;
25+
}
26+
27+
function applyTransaction<T extends Account>(account: T, tx: Transaction<T>): T {
28+
if (tx.amount < 0) throw new Error('Amount must be non-negative');
29+
if (tx.kind === 'withdraw' && account.balance < tx.amount) {
30+
throw new Error('Insufficient funds');
31+
}
32+
const balance = tx.kind === 'deposit'
33+
? account.balance + tx.amount
34+
: account.balance - tx.amount;
35+
return { ...account, balance };
36+
}
37+
38+
class Ledger<T extends Account> {
39+
private entries: Array<{ accountId: string; kind: TransactionKind; amount: number }>;
40+
41+
constructor() {
42+
this.entries = [];
43+
}
44+
45+
record(account: T, tx: Transaction<T>): T {
46+
const updated = applyTransaction(account, tx);
47+
const { kind, amount } = tx;
48+
const record = { accountId: account.id, kind, amount };
49+
this.entries.push(record);
50+
return updated;
51+
}
52+
53+
getAll() {
54+
return this.entries.slice();
55+
}
56+
}
57+
58+
// Usage
59+
60+
const savings: SavingsAccount = {
61+
id: 'S-1',
62+
balance: 1000,
63+
kind: 'savings',
64+
rate: 0.02,
65+
};
66+
67+
const credit: CreditAccount = {
68+
id: 'C-1',
69+
balance: 0,
70+
kind: 'credit',
71+
limit: 2000,
72+
};
73+
74+
const ledgerSavings = new Ledger<SavingsAccount>();
75+
const ledgerCredit = new Ledger<CreditAccount>();
76+
77+
const s1 = ledgerSavings.record(savings, { kind: 'deposit', amount: 200 });
78+
const s2 = ledgerSavings.record(s1, { kind: 'withdraw', amount: 50 });
79+
80+
const c1 = ledgerCredit.record(credit, { kind: 'deposit', amount: 500 });
81+
const c2 = ledgerCredit.record(c1, { kind: 'withdraw', amount: 150 });
82+
83+
console.dir({ s2, c2 });
84+
console.log('Savings entries:', ledgerSavings.getAll());
85+
console.log('Credit entries:', ledgerCredit.getAll());

Classification/a-generics.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
// Parametric polymorphism with Generics
4+
5+
const applyTransaction = (account, amount, kind) => {
6+
if (amount < 0) throw new Error('Amount must be non-negative');
7+
if (kind === 'withdraw' && account.balance < amount) {
8+
throw new Error('Insufficient funds');
9+
}
10+
const balance =
11+
kind === 'deposit' ? account.balance + amount : account.balance - amount;
12+
return { ...account, balance };
13+
};
14+
15+
class Ledger {
16+
constructor() {
17+
// Array of
18+
// { accountId: string, kind: 'deposit'|'withdraw', amount: number }
19+
this.entries = [];
20+
}
21+
22+
record(account, kind, amount) {
23+
const updated = applyTransaction(account, amount, kind);
24+
this.entries.push({ accountId: account.id, kind, amount });
25+
return updated;
26+
}
27+
}
28+
29+
// Usage
30+
31+
const savings = { id: 'S-1', balance: 1000, kind: 'savings', rate: 0.02 };
32+
const credit = { id: 'C-1', balance: 0, kind: 'credit', limit: 2000 };
33+
34+
const ledgerSavings = new Ledger();
35+
const ledgerCredit = new Ledger();
36+
37+
const s1 = ledgerSavings.record(savings, 'deposit', 200);
38+
const s2 = ledgerSavings.record(s1, 'withdraw', 50);
39+
40+
const c1 = ledgerCredit.record(credit, 'deposit', 500);
41+
const c2 = ledgerCredit.record(c1, 'withdraw', 150);
42+
43+
console.dir({ s2, c2 });
44+
console.log('Savings entries:', ledgerSavings.entries);
45+
console.log('Credit entries:', ledgerCredit.entries);

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
- Class inheritance - IS-A relationship via base classes: [`5-abstract.ts`](Classification/5-abstract.ts)
1414
- Interface / protocol polymorphism - Contracts that types must implement: [`6-protocol.js`](Classification/6-protocol.js)
1515
- Structural (duck typing) polymorphism - Compatibility based on structure, not explicit inheritance: [`6-protocol.js`](Classification/6-protocol.js)
16-
3. Parametric polymorphism - Code written generically to work with any type (examples will be added ASAP)
17-
- Generic functions - Functions parameterized by types
18-
- Generic data structures - Data structures parameterized by types
16+
3. Parametric polymorphism - Code written generically to work with any type
17+
- Generic functions - Functions parameterized by types: JS [`9-generics.ts`](Classification/9-generics.ts)
18+
- Generic data structures - Data structures parameterized by types: JS TS [`a-generics.js`](Classification/a-generics.js)
1919
4. Dispatch mechanisms - How the runtime selects which method to call
2020
- Dynamic dispatch - Method chosen at runtime based on object type: [`7-dynamic.js`](Classification/7-dynamic.js)
2121
- Virtual functions and methods - Base class methods overridable by derived classes: [`8-virtual.cpp`](Classification/8-virtual.cpp)

0 commit comments

Comments
 (0)