We use cookies to enhance your experience on the site
CodeWorlds

Final Project: Jurassic Park Diagnostic System

"We've reached the end of our journey," announces Dr. Ellie Sattler, standing in InGen's main diagnostic laboratory. "Throughout this module, you've mastered Web Workers, memory management, advanced TypeScript types — from utility types, through conditional and mapped types, to template literal types and module augmentation. You've also learned Test-Driven Development and TypeScript integration with popular frameworks."

"Now you'll build a complete Jurassic Park diagnostic system — a project that combines all these skills into one cohesive system. Think of it as a simulation of real, production code that could run in our park."

In this project, you'll create an advanced diagnostic system combining: expert-level typing with utility types and conditional types, template literal types for API generation, mapped types for data transformation, module augmentation for extending existing libraries, and a TDD approach with unit tests.

Project Goal

You'll build a

DiagnosticCenter
system that:

  • Uses advanced utility types for precise data modeling
  • Applies conditional types for automatic diagnostic strategy selection
  • Uses template literal types for building a type-safe API
  • Implements mapped types for diagnostic data transformation
  • Extends existing modules through module augmentation
  • Is written using a TDD approach with unit tests

Functional Requirements

1. Advanced Utility Types for Diagnostic Data

Define precise data types, using both built-in and custom utility types. The system must support different dinosaur categories with different sets of diagnostic parameters.

1// Base dinosaur type
2interface DinosaurRecord {
3  id: string;
4  species: string;
5  name: string;
6  age: number;
7  weight: number;
8  height: number;
9  enclosureId: string;
10  diet: 'carnivore' | 'herbivore' | 'omnivore';
11  healthStatus: 'healthy' | 'sick' | 'critical' | 'quarantined';
12  lastCheckup: Date;
13  geneticModifications: string[];
14  dangerRating: 1 | 2 | 3 | 4 | 5;
15}
16
17// Custom utility types
18type DeepReadonly<T> = {
19  readonly [K in keyof T]: T[K] extends object
20    ? T[K] extends Date | Array<any>
21      ? T[K]
22      : DeepReadonly<T[K]>
23    : T[K];
24};
25
26type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
27  Pick<T, Exclude<keyof T, Keys>> &
28  { [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];
29
30type Mutable<T> = {
31  -readonly [K in keyof T]: T[K];
32};
33
34// Locked record - cannot be modified after closing diagnostics
35type LockedDiagnosticRecord = DeepReadonly<DinosaurRecord>;
36
37// Update - require at least one field
38type DinosaurUpdate = RequireAtLeastOne<
39  Partial<Pick<DinosaurRecord, 'healthStatus' | 'weight' | 'enclosureId' | 'dangerRating'>>,
40  'healthStatus' | 'weight' | 'enclosureId' | 'dangerRating'
41>;
42
43// Display record - convert Date to string
44type DisplayRecord<T> = {
45  [K in keyof T]: T[K] extends Date ? string : T[K];
46};
47
48type DinosaurDisplayRecord = DisplayRecord<DinosaurRecord>;
49
50// Filtering numeric fields
51type NumericFields<T> = {
52  [K in keyof T as T[K] extends number ? K : never]: T[K];
53};
54
55type DinosaurMetrics = NumericFields<DinosaurRecord>;
56// Result: { age: number; weight: number; height: number; dangerRating: 1|2|3|4|5 }

2. Conditional Types for Diagnostic Strategies

Use conditional types to automatically determine the diagnostic procedure based on the dinosaur's species and health status. The system must "know" at the type level what tests to perform.

1// Species types
2type CarnivoreSpecies = 'TRex' | 'Velociraptor' | 'Spinosaurus' | 'Dilophosaurus';
3type HerbivoreSpecies = 'Triceratops' | 'Brachiosaurus' | 'Stegosaurus' | 'Parasaurolophus';
4type FlyingSpecies = 'Pteranodon' | 'Dimorphodon';
5type AquaticSpecies = 'Mosasaurus' | 'Plesiosaurus';
6
7type AllSpecies = CarnivoreSpecies | HerbivoreSpecies | FlyingSpecies | AquaticSpecies;
8
9// Conditional type - diagnostic procedure depends on species
10type DiagnosticProcedure<S extends AllSpecies> =
11  S extends CarnivoreSpecies ? {
12    type: 'carnivore-diagnostic';
13    sedationRequired: true;
14    aggressionTest: boolean;
15    teethInspection: boolean;
16    clawMeasurement: boolean;
17    huntingBehaviorAnalysis: boolean;
18  } :
19  S extends HerbivoreSpecies ? {
20    type: 'herbivore-diagnostic';
21    sedationRequired: false;
22    digestiveAnalysis: boolean;
23    hoofInspection: boolean;
24    grazingPatternCheck: boolean;
25  } :
26  S extends FlyingSpecies ? {
27    type: 'flying-diagnostic';
28    sedationRequired: true;
29    wingspanMeasurement: number;
30    flightCapabilityTest: boolean;
31    nestInspection: boolean;
32  } :
33  S extends AquaticSpecies ? {
34    type: 'aquatic-diagnostic';
35    sedationRequired: true;
36    underwaterExam: boolean;
37    gillFunction: boolean;
38    divingDepthTest: number;
39  } :
40  never;
41
42// Diagnostic result - depends on the procedure
43type DiagnosticResult<S extends AllSpecies> = {
44  species: S;
45  procedure: DiagnosticProcedure<S>;
46  timestamp: Date;
47  veterinarian: string;
48  passed: boolean;
49  notes: string;
50};
51
52// Usage - TypeScript enforces the correct fields
53const trexDiagnostic: DiagnosticResult<'TRex'> = {
54  species: 'TRex',
55  procedure: {
56    type: 'carnivore-diagnostic',
57    sedationRequired: true,
58    aggressionTest: true,
59    teethInspection: true,
60    clawMeasurement: true,
61    huntingBehaviorAnalysis: false
62  },
63  timestamp: new Date(),
64  veterinarian: 'Dr. Harding',
65  passed: true,
66  notes: 'T-Rex in excellent condition. Sedation went smoothly.'
67};
68
69// Conditional type with infer - extract the procedure type
70type ExtractProcedureType<T> =
71  T extends DiagnosticResult<infer S>
72    ? DiagnosticProcedure<S>
73    : never;
74
75// Distributive conditional type - filtering species requiring sedation
76type RequiresSedation<S extends AllSpecies> =
77  DiagnosticProcedure<S> extends { sedationRequired: true } ? S : never;
78
79type SedatedSpecies = RequiresSedation<AllSpecies>;
80// Result: 'TRex' | 'Velociraptor' | ... (all requiring sedation)

3. Template Literal Types for Type-safe API

Create an API system that guarantees correctness of endpoint names, configuration keys, and diagnostic messages at the type level.

1// Template literal types for API endpoints
2type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
3type ApiVersion = 'v1' | 'v2';
4type Resource = 'dinosaurs' | 'enclosures' | 'diagnostics' | 'alerts';
5
6// Automatic API path generation
7type ApiEndpoint = `/api/${ApiVersion}/${Resource}`;
8// Result: "/api/v1/dinosaurs" | "/api/v1/enclosures" | ... (12 combinations)
9
10type ApiEndpointWithId = `${ApiEndpoint}/${string}`;
11
12// Template literal types for configuration keys
13type SensorType = 'temperature' | 'heartRate' | 'movement' | 'stress';
14type SensorLocation = 'head' | 'body' | 'tail' | 'legs';
15
16type SensorConfigKey = `sensor_${SensorType}_${SensorLocation}`;
17// Result: "sensor_temperature_head" | "sensor_temperature_body" | ... (16 combinations)
18
19type SensorConfig = Record<SensorConfigKey, {
20  enabled: boolean;
21  threshold: number;
22  interval: number;
23}>;
24
25// Template literal types for diagnostic messages
26type DiagnosticStatus = 'started' | 'completed' | 'failed' | 'cancelled';
27type DiagnosticMessage<S extends AllSpecies, D extends DiagnosticStatus> =
28  `[DIAGNOSTIC] Species: ${S} | Status: ${D}`;
29
30// Usage
31const message: DiagnosticMessage<'TRex', 'completed'> =
32  '[DIAGNOSTIC] Species: TRex | Status: completed';
33
34// Extraction from template literal type
35type ExtractSpecies<T> = T extends `[DIAGNOSTIC] Species: ${infer S} | Status: ${string}`
36  ? S
37  : never;
38
39type ExtractedSpecies = ExtractSpecies<typeof message>; // "TRex"
40
41// Advanced: converting camelCase to kebab-case
42type CamelToKebab<S extends string> =
43  S extends `${infer First}${infer Rest}`
44    ? Rest extends Uncapitalize<Rest>
45      ? `${Lowercase<First>}${CamelToKebab<Rest>}`
46      : `${Lowercase<First>}-${CamelToKebab<Rest>}`
47    : S;
48
49type TestKebab = CamelToKebab<'healthStatus'>; // "health-status"
50type TestKebab2 = CamelToKebab<'dangerRating'>; // "danger-rating"

4. Mapped Types for Transformation and Type Generation

Use mapped types to automatically generate forms, validators, and diagnostic data transformations.

1// Mapped type: generate form type from data model
2type FormFields<T> = {
3  [K in keyof T]: {
4    value: T[K];
5    label: string;
6    required: boolean;
7    validate: (value: T[K]) => boolean;
8  };
9};
10
11type DinosaurForm = FormFields<Pick<DinosaurRecord, 'name' | 'species' | 'weight' | 'healthStatus'>>;
12
13// Mapped type: generate validator type
14type Validators<T> = {
15  [K in keyof T as `validate${Capitalize<K & string>}`]: (value: T[K]) => boolean;
16};
17
18type DinosaurValidators = Validators<Pick<DinosaurRecord, 'name' | 'age' | 'weight'>>;
19// Result: {
20//   validateName: (value: string) => boolean;
21//   validateAge: (value: number) => boolean;
22//   validateWeight: (value: number) => boolean;
23// }
24
25// Mapped type: generate getters and setters
26type Accessors<T> = {
27  [K in keyof T as `get${Capitalize<K & string>}`]: () => T[K];
28} & {
29  [K in keyof T as `set${Capitalize<K & string>}`]: (value: T[K]) => void;
30};
31
32type DinosaurAccessors = Accessors<Pick<DinosaurRecord, 'name' | 'healthStatus'>>;
33// Result: {
34//   getName: () => string;
35//   setName: (value: string) => void;
36//   getHealthStatus: () => 'healthy' | 'sick' | 'critical' | 'quarantined';
37//   setHealthStatus: (value: ...) => void;
38// }
39
40// Mapped type: convert values to Observable
41type Observable<T> = {
42  [K in keyof T]: {
43    value: T[K];
44    subscribe: (callback: (newValue: T[K], oldValue: T[K]) => void) => void;
45    unsubscribe: (callback: Function) => void;
46  };
47};
48
49type ObservableDinosaurMetrics = Observable<DinosaurMetrics>;
50
51// Mapped type implementation at runtime
52function createValidators<T extends Record<string, any>>(
53  rules: { [K in keyof T]: (value: T[K]) => boolean }
54): Validators<T> {
55  const validators = {} as any;
56  for (const key in rules) {
57    const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
58    validators[`validate${capitalizedKey}`] = rules[key];
59  }
60  return validators;
61}
62
63// Usage example
64const dinoValidators = createValidators<Pick<DinosaurRecord, 'name' | 'age' | 'weight'>>({
65  name: (value) => value.length >= 2 && value.length <= 50,
66  age: (value) => value > 0 && value < 200,
67  weight: (value) => value > 0 && value < 100000
68});

5. Module Augmentation and Test-Driven Development

Extend existing types through module augmentation and write the system using a TDD approach — tests first, then implementation.

1// Module augmentation - extending an existing module
2declare module './dinosaur-record' {
3  interface DinosaurRecord {
4    lastDiagnosticId?: string;
5    diagnosticHistory: DiagnosticEntry[];
6    riskScore: number;
7  }
8}
9
10interface DiagnosticEntry {
11  id: string;
12  date: Date;
13  type: string;
14  result: 'pass' | 'fail' | 'inconclusive';
15  veterinarian: string;
16}
17
18// TDD - write tests first
19// Test 1: DiagnosticCenter should calculate risk
20function testCalculateRiskScore() {
21  const center = new DiagnosticCenter();
22
23  // Healthy herbivore - low risk
24  const herbivoreRisk = center.calculateRiskScore({
25    diet: 'herbivore',
26    healthStatus: 'healthy',
27    dangerRating: 1,
28    age: 5
29  } as DinosaurRecord);
30
31  console.assert(herbivoreRisk < 30,
32    `Expected risk < 30, got ${herbivoreRisk}`);
33
34  // Sick carnivore - high risk
35  const carnivoreRisk = center.calculateRiskScore({
36    diet: 'carnivore',
37    healthStatus: 'sick',
38    dangerRating: 5,
39    age: 12
40  } as DinosaurRecord);
41
42  console.assert(carnivoreRisk > 70,
43    `Expected risk > 70, got ${carnivoreRisk}`);
44
45  console.log('testCalculateRiskScore: PASSED');
46}
47
48// Test 2: System should filter dinosaurs requiring checkup
49function testFilterDinosRequiringCheckup() {
50  const center = new DiagnosticCenter();
51
52  const dinosaurs: Partial<DinosaurRecord>[] = [
53    { name: 'Rex', lastCheckup: new Date('2024-01-01'), healthStatus: 'healthy' },
54    { name: 'Blue', lastCheckup: new Date('2025-12-01'), healthStatus: 'healthy' },
55    { name: 'Echo', lastCheckup: new Date('2025-12-15'), healthStatus: 'critical' }
56  ];
57
58  const needCheckup = center.filterRequiringCheckup(
59    dinosaurs as DinosaurRecord[],
60    30 // Days since last checkup
61  );
62
63  // Rex - checkup more than 30 days ago
64  // Echo - "critical" status requires immediate checkup
65  console.assert(needCheckup.length === 2,
66    `Expected 2 dinosaurs, got ${needCheckup.length}`);
67
68  console.log('testFilterDinosRequiringCheckup: PASSED');
69}
70
71// Test 3: Report generation should return the correct format
72function testGenerateReport() {
73  const center = new DiagnosticCenter();
74
75  const report = center.generateReport('TRex', {
76    species: 'TRex',
77    procedure: {
78      type: 'carnivore-diagnostic',
79      sedationRequired: true,
80      aggressionTest: true,
81      teethInspection: true,
82      clawMeasurement: true,
83      huntingBehaviorAnalysis: false
84    },
85    timestamp: new Date(),
86    veterinarian: 'Dr. Harding',
87    passed: true,
88    notes: 'Examination completed successfully'
89  });
90
91  console.assert(report.includes('[DIAGNOSTIC]'),
92    'Report should contain [DIAGNOSTIC] header');
93  console.assert(report.includes('TRex'),
94    'Report should contain species');
95  console.assert(report.includes('Dr. Harding'),
96    'Report should contain veterinarian');
97
98  console.log('testGenerateReport: PASSED');
99}
100
101// Implementation (after writing tests)
102class DiagnosticCenter {
103  calculateRiskScore(dino: Pick<DinosaurRecord, 'diet' | 'healthStatus' | 'dangerRating' | 'age'>): number {
104    let score = 0;
105
106    // Diet affects base risk
107    const dietScores = { carnivore: 30, omnivore: 15, herbivore: 5 };
108    score += dietScores[dino.diet];
109
110    // Health status
111    const healthScores = { healthy: 0, sick: 20, critical: 40, quarantined: 30 };
112    score += healthScores[dino.healthStatus];
113
114    // Danger level (1-5) * 6
115    score += dino.dangerRating * 6;
116
117    // Age - older dinosaurs are more unpredictable
118    if (dino.age > 10) score += 10;
119
120    return Math.min(100, Math.max(0, score));
121  }
122
123  filterRequiringCheckup(dinosaurs: DinosaurRecord[], maxDaysSinceCheckup: number): DinosaurRecord[] {
124    const now = new Date();
125    return dinosaurs.filter(dino => {
126      // Critical status - always requires checkup
127      if (dino.healthStatus === 'critical') return true;
128
129      // Check time since last checkup
130      const daysSinceCheckup = Math.floor(
131        (now.getTime() - dino.lastCheckup.getTime()) / (1000 * 60 * 60 * 24)
132      );
133      return daysSinceCheckup > maxDaysSinceCheckup;
134    });
135  }
136
137  generateReport<S extends AllSpecies>(
138    species: S,
139    result: DiagnosticResult<S>
140  ): string {
141    return [
142      `[DIAGNOSTIC] Species: ${species} | Status: ${result.passed ? 'completed' : 'failed'}`,
143      `Veterinarian: ${result.veterinarian}`,
144      `Date: ${result.timestamp.toISOString()}`,
145      `Result: ${result.passed ? 'POSITIVE' : 'NEGATIVE'}`,
146      `Notes: ${result.notes}`
147    ].join('\n');
148  }
149}
150
151// Running tests
152testCalculateRiskScore();
153testFilterDinosRequiringCheckup();
154testGenerateReport();

Implementation Guidelines

Start by defining base types and interfaces — this is the foundation of your diagnostic system. Then build conditional types for diagnostic procedures, template literal types for the API, and mapped types for transformations. Finally, write tests and implement the

DiagnosticCenter
.

Suggested implementation order:

1// 1. Base types: DinosaurRecord, AllSpecies
2// 2. Utility types: DeepReadonly, RequireAtLeastOne, NumericFields
3// 3. Conditional types: DiagnosticProcedure<S>, DiagnosticResult<S>
4// 4. Template literal types: ApiEndpoint, SensorConfigKey
5// 5. Mapped types: FormFields, Validators, Accessors
6// 6. Module augmentation: extending DinosaurRecord
7// 7. Unit tests (TDD)
8// 8. DiagnosticCenter implementation
9// 9. Integration and running tests

Remember the key principles:

  • Utility types are for transforming existing types — don't create new types from scratch when you can transform existing ones
  • Conditional types with
    infer
    allow extracting types from nested structures
  • Template literal types guarantee string correctness at compile time
  • Mapped types with
    as
    allow key name remapping
  • Module augmentation requires
    declare module
    and merges with the original type
  • TDD means: write test -> run (red) -> implement -> run (green) -> refactor

Good Luck!

"Remember," says Dr. Sattler with a smile, "in Jurassic Park there is no room for type errors. Every

any
is a potential security breach, every missing type is an open enclosure gate. TypeScript gives you the tools to catch 90% of bugs before the code even runs. Utility types, conditional types, mapped types, template literal types — these are your arsenal. Use them wisely."

"And tests? Tests are like alarm systems in enclosures. You don't build them after the T-Rex escapes. You build them BEFORE that happens. TDD is a philosophy that protects your code from catastrophe."

Open your editor and build your diagnostic system — complete, fully typed, and tested!

Go to CodeWorlds