"The time has come for a summary," announces Dr. Henry Wu, InGen's chief geneticist, standing before the enormous holographic screen of the laboratory. "Over the past weeks, you've mastered advanced TypeScript mechanisms - from access modifiers, through generics, to conditional types and mapped types. Now you'll build a complete Jurassic Park management system that combines all these elements into one cohesive whole."
In this project, you'll create an advanced type system for Jurassic Park. You'll use abstract classes, interfaces, generics, conditional types, mapped types, utility types, and decorators - everything you've learned in this module. The system will handle dinosaur registration, enclosure management, health monitoring, and security report generation.
You'll build a complete, fully typed
JurassicParkManager system that:Define an abstract base class
Dinosaur with protected properties and abstract methods. Then create interfaces describing different dinosaur abilities and implement them in derived classes.1// Ability interfaces
2interface IHerbivore {
3 graze(area: string): string;
4 preferredPlants: string[];
5}
6
7interface ICarnivore {
8 hunt(target: string): string;
9 attackPower: number;
10}
11
12interface IFlying {
13 fly(altitude: number): string;
14 maxAltitude: number;
15}
16
17interface ISwimming {
18 swim(depth: number): string;
19 maxDepth: number;
20}
21
22// Abstract class with access modifiers
23abstract class Dinosaur {
24 private static nextId: number = 1;
25
26 protected readonly id: number;
27 public readonly species: string;
28 protected health: number;
29 private enclosureId: string | null = null;
30
31 constructor(species: string, health: number = 100) {
32 this.id = Dinosaur.nextId++;
33 this.species = species;
34 this.health = health;
35 }
36
37 // Abstract method - each subtype defines its own danger level
38 abstract get dangerLevel(): 'low' | 'medium' | 'high' | 'critical';
39
40 // Abstract method - behavior description
41 abstract describeBehavior(): string;
42
43 // Static factory method
44 static getNextId(): number {
45 return Dinosaur.nextId;
46 }
47
48 // Public API
49 public getStatus(): string {
50 return `[${this.id}] ${this.species} - Health: ${this.health}%`;
51 }
52
53 public assignEnclosure(enclosureId: string): void {
54 this.enclosureId = enclosureId;
55 }
56
57 protected heal(amount: number): void {
58 this.health = Math.min(100, this.health + amount);
59 }
60}
61
62// Derived class implementing interfaces
63class TyrannosaurusRex extends Dinosaur implements ICarnivore {
64 public attackPower: number = 95;
65
66 constructor() {
67 super('Tyrannosaurus Rex', 100);
68 }
69
70 get dangerLevel(): 'critical' {
71 return 'critical';
72 }
73
74 describeBehavior(): string {
75 return 'Apex predator - requires maximum security';
76 }
77
78 hunt(target: string): string {
79 return `T-Rex hunts ${target} with attack power ${this.attackPower}`;
80 }
81}
82
83class Pteranodon extends Dinosaur implements ICarnivore, IFlying {
84 public attackPower: number = 40;
85 public maxAltitude: number = 500;
86
87 constructor() {
88 super('Pteranodon', 100);
89 }
90
91 get dangerLevel(): 'high' {
92 return 'high';
93 }
94
95 describeBehavior(): string {
96 return 'Flying predator - requires a roofed enclosure';
97 }
98
99 hunt(target: string): string {
100 return `Pteranodon dives at ${target} from the air`;
101 }
102
103 fly(altitude: number): string {
104 if (altitude > this.maxAltitude) {
105 return 'Altitude exceeds maximum ceiling!';
106 }
107 return `Pteranodon flies at altitude ${altitude}m`;
108 }
109}Create generic classes and functions with appropriate constraints to ensure type safety throughout the system.
1// Generic registry class with a constraint
2class ParkRegistry<T extends Dinosaur> {
3 private entries: Map<number, T> = new Map();
4
5 add(item: T): void {
6 this.entries.set(item['id'], item);
7 }
8
9 get(id: number): T | undefined {
10 return this.entries.get(id);
11 }
12
13 getAll(): T[] {
14 return Array.from(this.entries.values());
15 }
16
17 // Generic method with an additional constraint
18 filter<K extends keyof T>(key: K, value: T[K]): T[] {
19 return this.getAll().filter(item => item[key] === value);
20 }
21
22 // Method with multiple generic types
23 map<U>(transform: (item: T) => U): U[] {
24 return this.getAll().map(transform);
25 }
26}
27
28// Generic function with multiple constraints
29function transferDinosaur<
30 T extends Dinosaur,
31 S extends ParkRegistry<T>,
32 D extends ParkRegistry<T>
33>(source: S, destination: D, dinoId: number): boolean {
34 const dino = source.get(dinoId);
35 if (!dino) return false;
36
37 destination.add(dino);
38 return true;
39}
40
41// Generic interface for monitoring systems
42interface IMonitoringSystem<T extends Dinosaur> {
43 monitor(subject: T): MonitoringReport<T>;
44 getAlerts(): Alert<T>[];
45}
46
47// Generic report type
48interface MonitoringReport<T extends Dinosaur> {
49 subject: T;
50 timestamp: Date;
51 status: 'normall' | 'warning' | 'danger';
52 details: string;
53}
54
55interface Alert<T extends Dinosaur> {
56 dinosaur: T;
57 level: 'info' | 'warning' | 'critical';
58 message: string;
59}Use conditional types and mapped types to automatically adapt system behavior depending on the dinosaur type and danger level.
1// Conditional type - determines enclosure requirements based on danger level
2type DangerLevel = 'low' | 'medium' | 'high' | 'critical';
3
4type EnclosureRequirements<D extends DangerLevel> =
5 D extends 'critical' ? {
6 fenceVoltage: number;
7 guardCount: number;
8 cameraCount: number;
9 backupPower: boolean;
10 emergencyProtocol: string;
11 } :
12 D extends 'high' ? {
13 fenceVoltage: number;
14 guardCount: number;
15 cameraCount: number;
16 backupPower: boolean;
17 } :
18 D extends 'medium' ? {
19 fenceVoltage: number;
20 cameraCount: number;
21 } :
22 {
23 fenceType: string;
24 };
25
26// Using the conditional type
27const trexEnclosure: EnclosureRequirements<'critical'> = {
28 fenceVoltage: 10000,
29 guardCount: 4,
30 cameraCount: 12,
31 backupPower: true,
32 emergencyProtocol: 'Sector evacuation and dispatch of response team'
33};
34
35// Mapped types - generating readonly and optional versions
36type DinosaurData = {
37 species: string;
38 health: number;
39 location: string;
40 lastFed: Date;
41 dangerLevel: DangerLevel;
42};
43
44// Mapped type: display version (readonly)
45type ReadonlyDinosaurData = {
46 readonly [K in keyof DinosaurData]: DinosaurData[K];
47};
48
49// Mapped type: update version (everything optional)
50type DinosaurUpdate = {
51 [K in keyof DinosaurData]?: DinosaurData[K];
52};
53
54// Mapped type: extract only string fields
55type StringFieldsOnly<T> = {
56 [K in keyof T as T[K] extends string ? K : never]: T[K];
57};
58
59type DinosaurStringFields = StringFieldsOnly<DinosaurData>;
60// Result: { species: string; location: string; }
61
62// Advanced mapped type with conditional transformation
63type DinosaurDisplay<T> = {
64 [K in keyof T]: T[K] extends Date
65 ? string // Convert dates to strings
66 : T[K] extends number
67 ? `${number}%` // Numbers with percent
68 : T[K]; // Rest unchanged
69};
70
71// Conditional type with infer
72type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
73
74type DinosaurFactory = (species: string) => Dinosaur;
75type CreatedDino = ExtractReturnType<DinosaurFactory>; // Dinosaur
76
77// Distributive conditional type
78type ExcludeDangerLevels<T, U> = T extends U ? never : T;
79type SafeLevels = ExcludeDangerLevels<DangerLevel, 'high' | 'critical'>;
80// Result: 'low' | 'medium'Apply built-in utility types and create your own to precisely model operations on dinosaur data.
1// Built-in utility types
2type NewDinosaur = Omit<DinosaurData, 'lastFed'> & {
3 registrationDate: Date;
4};
5
6type DinosaurPreview = Pick<DinosaurData, 'species' | 'dangerLevel'>;
7
8type DinosaurRecord = Record<string, DinosaurData>;
9
10// Requiring fields that were previously optional
11type RequiredDinosaurUpdate = Required<DinosaurUpdate>;
12
13// Custom utility types
14type DeepReadonly<T> = {
15 readonly [K in keyof T]: T[K] extends object
16 ? T[K] extends Function
17 ? T[K]
18 : DeepReadonly<T[K]>
19 : T[K];
20};
21
22type DeepPartial<T> = {
23 [K in keyof T]?: T[K] extends object
24 ? T[K] extends Function
25 ? T[K]
26 : DeepPartial<T[K]>
27 : T[K];
28};
29
30// Type that excludes null and undefined from every field
31type NonNullableFields<T> = {
32 [K in keyof T]: NonNullable<T[K]>;
33};
34
35// Enclosure configuration system with full typing
36interface EnclosureConfig {
37 dimensions: {
38 width: number;
39 height: number;
40 depth: number;
41 };
42 security: {
43 fenceType: 'electric' | 'reinforced' | 'laser';
44 voltage?: number;
45 guardSchedule: string[];
46 };
47 environment: {
48 temperature: number;
49 humidity: number;
50 terrain: 'forest' | 'plains' | 'water' | 'mixed';
51 };
52}
53
54// Deep partial allows updating any arbitrarily nested field
55type EnclosureUpdate = DeepPartial<EnclosureConfig>;
56
57const partialUpdate: EnclosureUpdate = {
58 security: {
59 voltage: 15000 // Update only voltage
60 }
61};
62
63// Deep readonly for configuration that should not be changed
64type FrozenConfig = DeepReadonly<EnclosureConfig>;Start by defining base interfaces and types - this is the foundation of the entire system. Then create the abstract
Dinosaur class and several derived classes implementing different ability interfaces. Next, build a generic ParkRegistry and monitoring system. Finally, add conditional and mapped types that dynamically adapt the system's behavior.Remember the following file structure:
1// 1. Base types and interfaces
2// 2. Abstract Dinosaur class
3// 3. Derived classes (TRex, Velociraptor, Triceratops, Pteranodon...)
4// 4. Generic ParkRegistry<T>
5// 5. Monitoring system with conditional types
6// 6. Utility types and transformations
7// 7. Main JurassicParkManager class combining everythingExample of system initialization:
1// Create registry
2const carnivoreRegistry = new ParkRegistry<TyrannosaurusRex>();
3const herbivoreRegistry = new ParkRegistry<Triceratops>();
4
5// Add dinosaurs
6const rex = new TyrannosaurusRex();
7carnivoreRegistry.add(rex);
8
9// Enclosure configuration with conditional types
10const criticalEnclosure: EnclosureRequirements<'critical'> = {
11 fenceVoltage: 10000,
12 guardCount: 4,
13 cameraCount: 12,
14 backupPower: true,
15 emergencyProtocol: 'Code Red - full evacuation'
16};
17
18// Generate report
19const reports = carnivoreRegistry.map(dino => ({
20 species: dino.species,
21 status: dino.getStatus(),
22 danger: dino.dangerLevel
23}));
24
25console.log('=== Jurassic Park Report ===');
26reports.forEach(r => console.log(`${r.species}: ${r.status} [Danger: ${r.danger}]`));"Remember," says Dr. Wu, adjusting his glasses, "TypeScript's type system is your DNA - the foundation upon which you build the entire ecosystem. The better you design the types, the more stable your Park will be. Generics provide flexibility, conditional types - intelligence, and mapped types - the ability to transform. Combine them all and create a system that will stand the test of time... and the test of escaping dinosaurs."
Time to apply everything you've learned. Open the editor and build your advanced type system!