We use cookies to enhance your experience on the site
CodeWorlds

Final Project: Advanced Type System for Jurassic Park

"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.

Project Goal

You'll build a complete, fully typed

JurassicParkManager
system that:

  • Uses abstract classes as a base for different dinosaur categories
  • Implements interfaces defining contracts for park subsystems
  • Uses generics to create flexible data structures
  • Applies conditional types to dynamically determine behaviors
  • Utilizes mapped types for data transformation
  • Leverages utility types for precise modeling

Functional Requirements

1. Dinosaur Class Hierarchy with Abstract Classes and Interfaces

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}

2. Generic Data Structures with Constraints

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}

3. Conditional and Mapped Types for Dynamic Modeling

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'

4. Utility Types and Advanced Type Patterns

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>;

Implementation Tips

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 everything

Example 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}]`));

Good Luck!

"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!

Go to CodeWorlds