|
| 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()); |
0 commit comments