We use cookies to enhance your experience on the site
CodeWorlds

Interface definitions

Welcome back to Jurassic Park! Today, under the watchful eye of Dr. Henry Wu, we will explore a fundamental concept of TypeScript that is just as crucial for code organization as security protocols are for the park's proper operation - interfaces.

What are interfaces in TypeScript?

Interfaces in TypeScript define contracts in code and describe the structure of objects. They are like detailed protocols that specify what a given object should contain. In the context of Jurassic Park, interfaces can be compared to safety standards and handling protocols for different dinosaur species - each species has its own specific requirements and traits.

Basic interface syntax

We define an interface using the

interface
keyword:

1interface Dinosaur {
2  id: number;
3  name: string;
4  species: string;
5  age: number;
6  weight: number;
7}
8
9// We create an object that must satisfy the interface requirements
10const rex: Dinosaur = {
11  id: 1,
12  name: "Rex",
13  species: "Tyrannosaurus",
14  age: 7,
15  weight: 8000
16};
17
18// The following code would cause an error because required properties are missing
19// const raptor: Dinosaur = {
20//   id: 2,
21//   name: "Blue"
22// };

Optional properties

Some properties in an interface can be optional, marked by adding a question mark (

?
). It's like marking optional handling procedures for individual dinosaurs.

1interface DinosaurSpecimen {
2  id: number;
3  name: string;
4  species: string;
5  rfidTag?: string;         // Optional RFID tag
6  lastExamination?: Date;   // Optional date of last examination
7  notes?: string;           // Optional research notes
8}
9
10// Object satisfying the interface, but without all optional properties
11const specimen1: DinosaurSpecimen = {
12  id: 101,
13  name: "Specimen A",
14  species: "Velociraptor"
15};
16
17// Object with some optional properties
18const specimen2: DinosaurSpecimen = {
19  id: 102,
20  name: "Specimen B",
21  species: "Dilophosaurus",
22  rfidTag: "DILO-102",
23  notes: "Shows aggressive behavior in the presence of new objects"
24};

Read-only (readonly)

Some properties should only be modified when the object is created. In TypeScript we can achieve this using the

readonly
modifier. It's like safety procedures that, once established, cannot be changed during normal operations.

1interface GeneticCode {
2  readonly id: string;
3  readonly sequence: string;
4  readonly donorSpecies: string;
5  modifications: string[];
6  isolationDate: Date;
7}
8
9// Creating genetic code
10const trexCode: GeneticCode = {
11  id: "GEN-TREX-001",
12  sequence: "ACGTTGCAAGCTA...",
13  donorSpecies: "West African frog",
14  modifications: ["Gap DNA filling", "Aggression gene correction"],
15  isolationDate: new Date("2022-05-15")
16};
17
18// We can add new modifications
19trexCode.modifications.push("Immune resistance enhancement");
20
21// But we cannot change the values of readonly fields
22// trexCode.id = "GEN-TREX-002"; // Error: Cannot assign to 'id' because it is a read-only property
23// trexCode.sequence = "GCTA..."; // Error: Cannot assign to 'sequence' because it is a read-only property

Function interfaces

Interfaces can also define function types, which is useful when describing callbacks or functions passed as parameters. In the context of the park, it's like defining standard procedures for handling different situations.

1// Security check function interface
2interface SecurityCheck {
3  (threatLevel: number, parkZone: string): boolean;
4}
5
6// Implementation of a function conforming to the interface
7const checkAccess: SecurityCheck = function(level, zone) {
8  // Access checking logic
9  if (zone === "T-Rex Enclosure" && level < 5) {
10    return false; // Deny access to T-Rex zone for staff below level 5
11  }
12
13  if (zone.includes("Laboratory") && level < 4) {
14    return false; // Deny access to laboratories for staff below level 4
15  }
16
17  return true; // Access permitted for other cases
18};
19
20// Using the function
21console.log(checkAccess(3, "T-Rex Enclosure")); // false
22console.log(checkAccess(5, "T-Rex Enclosure")); // true
23console.log(checkAccess(3, "Public area")); // true

Indexable types

Interfaces can define indexable types - useful when we have objects with a dynamic number of properties of the same type. In the context of the park, this could be a dinosaur database indexed by their identifiers.

1// Interface for a dinosaur database
2interface DinosaursDatabase {
3  [id: string]: {
4    name: string;
5    species: string;
6    age: number;
7    health: "good" | "needs attention" | "critical";
8  };
9}
10
11// Creating the database
12const database: DinosaursDatabase = {
13  "TREX-001": {
14    name: "Rex",
15    species: "Tyrannosaurus",
16    age: 7,
17    health: "good"
18  },
19  "VELOC-001": {
20    name: "Blue",
21    species: "Velociraptor",
22    age: 4,
23    health: "good"
24  },
25  "TRIC-003": {
26    name: "Trike",
27    species: "Triceratops",
28    age: 10,
29    health: "needs attention"
30  }
31};
32
33// Accessing data
34console.log(database["VELOC-001"].name); // "Blue"
35
36// Adding a new entry
37database["STEGO-002"] = {
38  name: "Spike",
39  species: "Stegosaurus",
40  age: 6,
41  health: "good"
42};

Class interfaces

Interfaces can also define contracts that classes must implement. It's like operational standards that every department in the park must meet.

1// Interface for security systems
2interface SecuritySystem {
3  name: string;
4  isActive: boolean;
5  securityLevel: number;
6
7  activate(): void;
8  deactivate(): void;
9  checkStatus(): string;
10}
11
12// Class implementing the interface
13class ElectronicFenceSystem implements SecuritySystem {
14  name: string;
15  isActive: boolean;
16  securityLevel: number;
17  voltage: number; // Additional property specific to this class
18
19  constructor(name: string, securityLevel: number, voltage: number) {
20    this.name = name;
21    this.isActive = false; // Initially inactive
22    this.securityLevel = securityLevel;
23    this.voltage = voltage;
24  }
25
26  activate(): void {
27    this.isActive = true;
28    console.log(`System ${this.name} activated. Voltage: ${this.voltage}V`);
29  }
30
31  deactivate(): void {
32    this.isActive = false;
33    console.log(`System ${this.name} deactivated.`);
34  }
35
36  checkStatus(): string {
37    return this.isActive
38      ? `${this.name}: ACTIVE (Level ${this.securityLevel})`
39      : `${this.name}: INACTIVE`;
40  }
41
42  // Method specific to this class
43  increaseVoltage(by: number): void {
44    this.voltage += by;
45    console.log(`Voltage increased to ${this.voltage}V`);
46  }
47}
48
49// Using the class
50const trexFence = new ElectronicFenceSystem("T-Rex Fence", 5, 10000);
51trexFence.activate();
52console.log(trexFence.checkStatus());
53trexFence.increaseVoltage(2000);

Extending interfaces

Interfaces can extend other interfaces, similar to how in biology species share common traits but also have their own unique properties.

1// Basic interface for all animals in the park
2interface Animal {
3  id: number;
4  species: string;
5  age: number;
6  weight: number;
7}
8
9// Interface for dinosaurs extending the Animal interface
10interface Dinosaur extends Animal {
11  era: string; // e.g. "Cretaceous", "Jurassic"
12  isPredator: boolean;
13  mainDiet: string[];
14}
15
16// Interface for predatory dinosaurs extending the Dinosaur interface
17interface PredatoryDinosaur extends Dinosaur {
18  biteForce: number;  // in PSI
19  runSpeed: number;   // in km/h
20  huntingBehavior: string[];
21}
22
23// Creating an object conforming to the PredatoryDinosaur interface
24const velociraptor: PredatoryDinosaur = {
25  id: 101,
26  species: "Velociraptor",
27  age: 4,
28  weight: 70,
29  era: "Cretaceous",
30  isPredator: true,
31  mainDiet: ["small mammals", "lizards", "other small dinosaurs"],
32  biteForce: 1000,
33  runSpeed: 60,
34  huntingBehavior: ["pack hunting", "ambush", "chase"]
35};

Declaration merging for interfaces

In TypeScript it is possible to define the same interface multiple times - the declarations will be merged. This is useful when we want to add new properties to an existing interface.

1// Initial definition of the security protocol
2interface SecurityProtocol {
3  name: string;
4  threatLevel: number;
5  evacuationProcedure: string;
6}
7
8// Extending the protocol with additional properties
9interface SecurityProtocol {
10  responsibleStaff: string[];
11  responseTime: number; // in minutes
12}
13
14// The object must contain all properties from both declarations
15const alphaProtocol: SecurityProtocol = {
16  name: "Protocol Alpha",
17  threatLevel: 3,
18  evacuationProcedure: "Evacuation towards the main control center",
19  responsibleStaff: ["Dr. Wu", "Owen Grady", "Claire Dearing"],
20  responseTime: 15
21};

Interfaces vs types

TypeScript also offers the

type
keyword for defining types. Although in many cases interfaces and types can be used interchangeably, there are differences between them:

1// Definition using interface
2interface EvacuationPlan {
3  route: string[];
4  estimatedTime: number;
5  assemblyPoints: string[];
6}
7
8// Equivalent definition using type
9type EvacuationPlanType = {
10  route: string[];
11  estimatedTime: number;
12  assemblyPoints: string[];
13};
14
15// Both work similarly for basic use
16const planA: EvacuationPlan = {
17  route: ["Operations center", "Main Street", "Evacuation hall"],
18  estimatedTime: 25,
19  assemblyPoints: ["Operations center", "Evacuation hall"]
20};
21
22const planB: EvacuationPlanType = {
23  route: ["Gallimimus Enclosure", "Innovation Center", "Evacuation hall"],
24  estimatedTime: 30,
25  assemblyPoints: ["Innovation Center", "Evacuation hall"]
26};

Key differences between interface and type:

  1. Extensibility:

    • Interfaces can be extended by multiple declarations with the same name
    • Types cannot be "opened" - once declared, a type cannot be extended
  2. Complex types:

    • Types can directly represent primitive types, unions, intersections
    • Interfaces are more focused on defining the shape of objects
1// Only possible with types
2type DinosaurID = string | number;
3type EnclosureCoordinates = [number, number, string]; // Tuple
4type DinosaurStatus = "active" | "resting" | "sleeping"; // Union of literals
5
6// More natural with interfaces
7interface Procedure {
8  name: string;
9  steps: string[];
10  executionTime: number;
11  responsibleStaff: string[];
12}

Practical application of interfaces

Let's look at how we can use interfaces to model the Jurassic Park management system:

1// Basic interfaces
2interface ParkPerson {
3  id: string;
4  firstName: string;
5  lastName: string;
6  accessLevel: 1 | 2 | 3 | 4 | 5;
7  hireDate: Date;
8}
9
10interface Location {
11  id: string;
12  name: string;
13  type: "enclosure" | "laboratory" | "operations center" | "public zone";
14  coordinates: [number, number]; // [latitude, longitude]
15  requiredAccessLevel: 1 | 2 | 3 | 4 | 5;
16}
17
18interface DinosaurSpecies {
19  name: string;
20  geologicalEra: string;
21  type: "herbivore" | "predator" | "omnivore";
22  averageWeight: number;
23  averageAge: number;
24  dangerLevel: 1 | 2 | 3 | 4 | 5;
25  dietRequirements: string[];
26  habitatRequirements: string[];
27}
28
29interface DinosaurSpecimen {
30  id: string;
31  species: string;
32  firstName?: string;
33  age: number;
34  weight: number;
35  gender: "male" | "female";
36  location: string; // Location ID
37  healthStatus: "excellent" | "good" | "needs attention" | "sick" | "critical";
38  lastExamination: Date;
39  rfidTag: string;
40  notes?: string;
41}
42
43// Interfaces for management systems
44interface SpecimenTrackingSystem {
45  getPosition(specimenId: string): [number, number];
46  trackMovement(specimenId: string, trackingTime: number): [number, number][];
47  checkIfInZone(specimenId: string, zoneId: string): boolean;
48  alertWhenOutsideZone(specimenId: string, callback: (specimen: DinosaurSpecimen) => void): void;
49}
50
51interface EmployeeManagementSystem {
52  addEmployee(employee: ParkPerson): void;
53  removeEmployee(id: string): boolean;
54  grantAccess(employeeId: string, level: 1 | 2 | 3 | 4 | 5): boolean;
55  checkPresenceInZone(employeeId: string, zoneId: string): boolean;
56}
57
58// Example implementation
59class ParkManagementSystem {
60  private employees: ParkPerson[] = [];
61  private locations: Location[] = [];
62  private species: DinosaurSpecies[] = [];
63  private specimens: DinosaurSpecimen[] = [];
64
65  // Methods for staff
66  addEmployee(employee: ParkPerson): void {
67    this.employees.push(employee);
68    console.log(`Added employee: ${employee.firstName} ${employee.lastName}`);
69  }
70
71  // Methods for locations
72  addLocation(location: Location): void {
73    this.locations.push(location);
74    console.log(`Added location: ${location.name}`);
75  }
76
77  // Methods for species
78  addSpecies(species: DinosaurSpecies): void {
79    this.species.push(species);
80    console.log(`Added species: ${species.name}`);
81  }
82
83  // Methods for specimens
84  addSpecimen(specimen: DinosaurSpecimen): void {
85    this.specimens.push(specimen);
86    console.log(`Added specimen: ${specimen.id} (${specimen.species})`);
87  }
88
89  // Management methods
90  checkAccess(employeeId: string, locationId: string): boolean {
91    const employee = this.employees.find(p => p.id === employeeId);
92    const location = this.locations.find(l => l.id === locationId);
93
94    if (!employee || !location) return false;
95
96    return employee.accessLevel >= location.requiredAccessLevel;
97  }
98
99  findSpecimensBySpecies(speciesName: string): DinosaurSpecimen[] {
100    return this.specimens.filter(o => o.species === speciesName);
101  }
102
103  findSpecimensInZone(zoneId: string): DinosaurSpecimen[] {
104    return this.specimens.filter(o => o.location === zoneId);
105  }
106
107  generateParkStatusReport(): {
108    employeeCount: number;
109    locationCount: number;
110    speciesCount: number;
111    specimenCount: number;
112    specimenHealthStatus: {
113      excellent: number;
114      good: number;
115      needsAttention: number;
116      sick: number;
117      critical: number;
118    }
119  } {
120    // Analyzing health status of all specimens
121    const healthStatus = {
122      excellent: 0,
123      good: 0,
124      needsAttention: 0,
125      sick: 0,
126      critical: 0
127    };
128
129    this.specimens.forEach(specimen => {
130      if (specimen.healthStatus === "excellent") healthStatus.excellent++;
131      else if (specimen.healthStatus === "good") healthStatus.good++;
132      else if (specimen.healthStatus === "needs attention") healthStatus.needsAttention++;
133      else if (specimen.healthStatus === "sick") healthStatus.sick++;
134      else if (specimen.healthStatus === "critical") healthStatus.critical++;
135    });
136
137    return {
138      employeeCount: this.employees.length,
139      locationCount: this.locations.length,
140      speciesCount: this.species.length,
141      specimenCount: this.specimens.length,
142      specimenHealthStatus: healthStatus
143    };
144  }
145}
146
147// Example usage
148const jurassicPark = new ParkManagementSystem();
149
150// Adding a location
151jurassicPark.addLocation({
152  id: "LOC-001",
153  name: "T-Rex Enclosure",
154  type: "enclosure",
155  coordinates: [23.45, -67.89],
156  requiredAccessLevel: 4
157});
158
159// Adding a species
160jurassicPark.addSpecies({
161  name: "Tyrannosaurus Rex",
162  geologicalEra: "Cretaceous",
163  type: "predator",
164  averageWeight: 7000,
165  averageAge: 30,
166  dangerLevel: 5,
167  dietRequirements: ["large amounts of meat", "calcium supplementation"],
168  habitatRequirements: ["large area", "natural vegetation", "water access"]
169});
170
171// Adding an employee
172jurassicPark.addEmployee({
173  id: "EMP-001",
174  firstName: "Owen",
175  lastName: "Grady",
176  accessLevel: 5,
177  hireDate: new Date("2010-06-15")
178});
179
180// Adding a specimen
181jurassicPark.addSpecimen({
182  id: "DINO-001",
183  species: "Tyrannosaurus Rex",
184  firstName: "Rexy",
185  age: 7,
186  weight: 7500,
187  gender: "female",
188  location: "LOC-001",
189  healthStatus: "good",
190  lastExamination: new Date("2023-03-20"),
191  rfidTag: "RFID-TREX-001"
192});
193
194// Generating a report
195const report = jurassicPark.generateParkStatusReport();
196console.log("Jurassic Park Status Report:");
197console.log(`Number of employees: ${report.employeeCount}`);
198console.log(`Number of locations: ${report.locationCount}`);
199console.log(`Number of species: ${report.speciesCount}`);
200console.log(`Number of specimens: ${report.specimenCount}`);
201console.log("Specimen health status:", report.specimenHealthStatus);

Summary

Interfaces in TypeScript are a powerful tool for modeling data structures and defining contracts in code. In the context of managing Jurassic Park, they allow us to precisely define what the various systems, procedures and park elements should look like.

| Interface feature | Use in Jurassic Park | |-------------------|----------------------| | Basic definition | Data structures for dinosaurs, employees, locations | | Optional properties | Additional information that may or may not be available | | Readonly properties | Data that should not be changed, e.g. genetic IDs | | Function interfaces | Definitions of safety procedures and protocols | | Indexable types | Dynamic databases of dinosaurs and resources | | Class interfaces | Standards for security and monitoring systems | | Extending interfaces | Inheritance of traits between dinosaur species | | Declaration merging | Extending protocols with additional procedures |

Dr. Wu summarizes: "Interfaces in TypeScript, like our park protocols, ensure that every element of the system works as expected. They allow us to build complex systems with confidence that individual components will work together - which is absolutely crucial when dealing with species that can be dangerous if something goes wrong."

Go to CodeWorlds