We use cookies to enhance your experience on the site
CodeWorlds

Type aliases

Welcome back to the Jurassic Park genetics laboratory! Dr. Henry Wu will today introduce us to an extremely useful TypeScript mechanism - type aliases. Just as scientists in the park use shorthand names for complex genetic sequences, programmers use type aliases to simplify complex type definitions.

What are type aliases?

A type alias is simply a name we give to an existing type. We can alias both simple and complex types, which makes code more readable and easier to maintain. It's like naming different DNA mixtures so they're easier to refer to during lab work.

Basic syntax

We define type aliases using the

type
keyword:

1// Simple type alias for string
2type SpeciesID = string;
3
4// Using the alias
5const tRexID: SpeciesID = "TREX-01";
6const velociraptorID: SpeciesID = "VELOC-01";

Aliases for primitive types

Type aliases can be applied to simple types, which can be helpful for documentation and code intent:

1// Aliases for simple types
2type SecurityLevel = number;
3type SpeciesName = string;
4type IsPredator = boolean;
5
6// Function using type aliases
7function setEnclosureLevel(species: SpeciesName, predator: IsPredator): SecurityLevel {
8  if (predator) {
9    if (species === "Tyrannosaurus") return 5;
10    if (species === "Velociraptor") return 4;
11    return 3; // Other predators
12  } else {
13    return 2; // Herbivores
14  }
15}
16
17const trexLevel = setEnclosureLevel("Tyrannosaurus", true); // 5
18console.log(`Security level for T-Rex: ${trexLevel}`);

Aliases for complex types

Type aliases become especially useful when defining complex types such as objects, unions or arrays:

1// Alias for object type (similar to interface)
2type Dinosaur = {
3  id: string;
4  name: string;
5  species: string;
6  age: number;
7  weight: number;
8  predator: boolean;
9};
10
11// Alias for union type - different identifiers
12type DinosaurID = string | number;
13
14// Alias for tuple - GPS coordinates with description
15type Location = [number, number, string];
16
17// Alias for array
18type SpeciesList = string[];
19
20// Alias for an object literal with optional fields
21type HealthStatus = {
22  overallHealth: "excellent" | "good" | "average" | "poor" | "critical";
23  temperature?: number;
24  heartRate?: number;
25  treatmentPriority?: 1 | 2 | 3 | 4 | 5;
26  notes?: string;
27};
28
29// Creating objects based on type aliases
30const rexLocation: Location = [35.6895, 139.6917, "T-Rex Enclosure, northern fence"];
31const parkSpecies: SpeciesList = ["Tyrannosaurus", "Velociraptor", "Triceratops", "Brachiosaurus"];
32
33const rexStatus: HealthStatus = {
34  overallHealth: "good",
35  temperature: 38.2,
36  treatmentPriority: 2,
37  notes: "Recently slightly reduced appetite"
38};
39
40// Function working on type alias
41function prepareDinosaurReport(dino: Dinosaur, status: HealthStatus, location: Location): string {
42  const [lat, lng, description] = location;
43
44  return `
45    Dinosaur report:
46    ID: ${dino.id}
47    Name: ${dino.name}
48    Species: ${dino.species}
49    Age: ${dino.age} years
50    Weight: ${dino.weight} kg
51    Predator: ${dino.predator ? "Yes" : "No"}
52
53    Health status: ${status.overallHealth}
54    ${status.temperature ? `Temperature: ${status.temperature}°C` : ""}
55    ${status.heartRate ? `Heart rate: ${status.heartRate} beats/min` : ""}
56
57    Location: ${description} (${lat}, ${lng})
58  `;
59}

Aliases with generic types

Type aliases can also be generic, giving us even greater flexibility:

1// Generic type alias for monitoring system data
2type MonitoringData<T> = {
3  measurementTime: Date;
4  value: T;
5  unit: string;
6  valid: boolean;
7};
8
9// Using the generic type alias for different types of measurements
10type TemperatureMeasurement = MonitoringData<number>;
11type ActivityMeasurement = MonitoringData<"low" | "medium" | "high">;
12type StressLevelMeasurement = MonitoringData<1 | 2 | 3 | 4 | 5>;
13
14// Function analyzing monitoring data
15function analyzeData<T>(data: MonitoringData<T>): void {
16  console.log(`Measurement from ${data.measurementTime.toISOString()}`);
17  console.log(`Value: ${data.value} ${data.unit}`);
18  console.log(`Status: ${data.valid ? "Valid" : "Needs attention"}`);
19}
20
21// Sample usage
22const waterTemperature: TemperatureMeasurement = {
23  measurementTime: new Date(),
24  value: 24.5,
25  unit: "°C",
26  valid: true
27};
28
29const trexActivityLevel: ActivityMeasurement = {
30  measurementTime: new Date(),
31  value: "high",
32  unit: "activity level",
33  valid: false // Needs attention because T-Rex is too active
34};
35
36analyzeData(waterTemperature);
37analyzeData(trexActivityLevel);

Recursive type aliases

Type aliases can be recursive, which is useful for modeling tree structures such as dinosaur species hierarchies or park organizational structures:

1// Recursive type alias for dinosaur category hierarchy
2type DinosaursCategory = {
3  name: string;
4  description?: string;
5  subcategories?: DinosaursCategory[];
6};
7
8// Creating a category hierarchy
9const taxonomy: DinosaursCategory = {
10  name: "Dinosaurs",
11  description: "Main classification of dinosaurs in Jurassic Park",
12  subcategories: [
13    {
14      name: "Theropods",
15      description: "Carnivorous, bipedal dinosaurs",
16      subcategories: [
17        {
18          name: "Tyrannosaurids",
19          subcategories: [
20            { name: "Tyrannosaurus Rex" }
21          ]
22        },
23        {
24          name: "Deinonychosaurs",
25          subcategories: [
26            { name: "Velociraptor" },
27            { name: "Deinonychus" }
28          ]
29        }
30      ]
31    },
32    {
33      name: "Sauropods",
34      description: "Large, four-legged herbivores with long necks",
35      subcategories: [
36        { name: "Brachiosaurus" },
37        { name: "Diplodocus" }
38      ]
39    }
40  ]
41};
42
43// Function to render the taxonomy tree
44function displayTaxonomy(category: DinosaursCategory, level: number = 0): void {
45  const indent = "  ".repeat(level);
46  console.log(`${indent}${category.name}`);
47
48  if (category.description) {
49    console.log(`${indent}  Description: ${category.description}`);
50  }
51
52  if (category.subcategories && category.subcategories.length > 0) {
53    category.subcategories.forEach(subcategory => {
54      displayTaxonomy(subcategory, level + 1);
55    });
56  }
57}
58
59displayTaxonomy(taxonomy);

Type aliases vs interfaces

Type aliases are often compared to interfaces because in many cases they can be used interchangeably. However, there are some key differences between them:

1// Structure definition using interface
2interface DinosaurInterface {
3  id: string;
4  name: string;
5  species: string;
6}
7
8// Equivalent definition using type alias
9type DinosaurType = {
10  id: string;
11  name: string;
12  species: string;
13};
14
15// Both work similarly for basic use
16const dino1: DinosaurInterface = { id: "001", name: "Rex", species: "Tyrannosaurus" };
17const dino2: DinosaurType = { id: "002", name: "Blue", species: "Velociraptor" };

Key differences between type aliases and interfaces

  1. Extensibility:

    • Interfaces can be extended by declaring them multiple times
    • Type aliases cannot be "opened" - once defined, an alias cannot be extended later
    1// Interface can be extended by multiple declarations
    2interface AnimalInterface {
    3  id: string;
    4  species: string;
    5}
    6
    7// Adding new fields to the existing interface
    8interface AnimalInterface {
    9  wiek: number;
    10  waga: number;
    11}
    12
    13// Now AnimalInterface has all four fields
    14const animal: AnimalInterface = {
    15  id: "001",
    16  species: "Tyrannosaurus",
    17  wiek: 7,
    18  weight: 7000
    19};
    20
    21// A type alias cannot be extended this way
    22type AnimalType = {
    23  id: string;
    24  species: string;
    25}
    26
    27// This would cause an error: Duplicate identifier 'AnimalType'
    28// type AnimalType = {
    29//   age: number;
    30//   weight: number;
    31// }
  2. Complex types:

    • Type aliases can directly represent unions, intersections and other advanced types
    • Interfaces are more focused on defining the shape of objects
    1// Only possible with type aliases
    2type SecurityStatus = "normal" | "elevated" | "threat";
    3type GPSCoordinates = [number, number];
    4type AnimalID = string | number;
    5
    6// With interfaces this would be more cumbersome
  3. Interfaces for classes:

    • Interfaces are the natural choice for defining contracts that classes must implement
    • Type aliases are more flexible for defining complex types
    1// Interface for a class
    2interface MonitoringSystem {
    3  start(): void;
    4  stop(): void;
    5  getStatus(): string;
    6}
    7
    8// Class implementing the interface
    9class EnclosureMonitoring implements MonitoringSystem {
    10  start() { console.log("Enclosure monitoring started"); }
    11  stop() { console.log("Enclosure monitoring stopped"); }
    12  getStatus() { return "Active"; }
    13}

Combining type aliases with other TypeScript features

Type aliases can be combined with other TypeScript features, creating powerful abstractions:

1// Combining with union types
2type DinosaurData =
3  | { type: "basic"; id: string; species: string }
4  | { type: "extended"; id: string; species: string; age: number; weight: number }
5  | { type: "full"; id: string; species: string; age: number; weight: number; history: string[]; healthStatus: string };
6
7// Function processing different levels of data
8function processDinosaurData(data: DinosaurData): void {
9  console.log(`Processing data for ID: ${data.id}, species: ${data.species}`);
10
11  // We use the discriminated union to determine the data type
12  switch (data.type) {
13    case "basic":
14      console.log("Processing basic data");
15      break;
16    case "extended":
17      console.log(`Age: ${data.age}, Weight: ${data.weight} kg`);
18      break;
19    case "full":
20      console.log(`Age: ${data.age}, Weight: ${data.weight} kg`);
21      console.log(`History: ${data.history.join(", ")}`);
22      console.log(`Health status: ${data.healthStatus}`);
23      break;
24  }
25}
26
27// Usage examples
28const basic: DinosaurData = {
29  type: "basic",
30  id: "TRX-001",
31  species: "Tyrannosaurus"
32};
33
34const extended: DinosaurData = {
35  type: "extended",
36  id: "VEL-001",
37  species: "Velociraptor",
38  age: 4,
39  weight: 150
40};
41
42const full: DinosaurData = {
43  type: "full",
44  id: "TRI-001",
45  species: "Triceratops",
46  age: 10,
47  weight: 8000,
48  history: ["Born: 2013", "Transfer from Location B: 2016", "Broken horns: 2018", "Recovery: 2019"],
49  healthStatus: "Good"
50};
51
52processDinosaurData(basic);
53processDinosaurData(extended);
54processDinosaurData(full);

Combining utility types with type aliases

TypeScript offers built-in utility types that can be combined with type aliases, giving us even greater data modeling capabilities:

1// Base interface for a dinosaur
2interface Dinosaur {
3  id: string;
4  name: string;
5  species: string;
6  age: number;
7  weight: number;
8  gender: "male" | "female";
9  predator: boolean;
10  nocturnal: boolean;
11  healthStatus: "excellent" | "good" | "needs attention" | "sick" | "critical";
12}
13
14// Type aliases using utility types
15
16// Read-only type - useful for data that should not be modified
17type DinosaurReadOnly = Readonly<Dinosaur>;
18
19// Type with selected fields - useful for partial data
20type DinosaurBasic = Pick<Dinosaur, "id" | "name" | "species">;
21
22// Type with omitted fields - useful when we want to exclude certain data
23type DinosaurWithoutStatus = Omit<Dinosaur, "healthStatus">;
24
25// Type with optional fields - useful for partial updates
26type DinosaurPartial = Partial<Dinosaur>;
27
28// Type with required fields - forcing all fields to be defined
29type DinosaurRequired = Required<Dinosaur>;
30
31// Record type - useful for key-indexed collections
32type DinosaursDatabase = Record<string, Dinosaur>;
33
34// Usage examples
35const rexReadOnly: DinosaurReadOnly = {
36  id: "TRX-001",
37  name: "Rex",
38  species: "Tyrannosaurus",
39  age: 7,
40  weight: 7000,
41  gender: "female",
42  predator: true,
43  nocturnal: false,
44  healthStatus: "good"
45};
46
47// rexReadOnly.age = 8; // Error: Cannot assign to 'age' because it is a read-only property
48
49const rexBasic: DinosaurBasic = {
50  id: "TRX-001",
51  name: "Rex",
52  species: "Tyrannosaurus"
53};
54
55// Partial data update
56function updateDinosaurData(id: string, data: DinosaurPartial): void {
57  console.log(`Updating data for dinosaur ${id}`);
58  console.log(data);
59  // In a real application we would update the data in the database
60}
61
62updateDinosaurData("TRX-001", {
63  age: 8,
64  weight: 7200,
65  healthStatus: "needs attention"
66});
67
68// Dinosaur database
69const database: DinosaursDatabase = {
70  "TRX-001": {
71    id: "TRX-001",
72    name: "Rex",
73    species: "Tyrannosaurus",
74    age: 7,
75    weight: 7000,
76    gender: "female",
77    predator: true,
78    nocturnal: false,
79    healthStatus: "good"
80  },
81  "VEL-001": {
82    id: "VEL-001",
83    name: "Blue",
84    species: "Velociraptor",
85    age: 4,
86    weight: 150,
87    gender: "female",
88    predator: true,
89    nocturnal: true,
90    healthStatus: "excellent"
91  }
92};
93
94console.log(`Number of dinosaurs in the database: ${Object.keys(database).length}`);

Practical example of the Jurassic Park data model

Let's now see how we can use type aliases together with other TypeScript features to create a comprehensive data model for the Jurassic Park management system:

1// Base types
2type ID = string;
3type Coordinates = [number, number];
4type AccessLevel = 1 | 2 | 3 | 4 | 5;
5type ActivityStatus = "active" | "inactive" | "under maintenance";
6type HealthStatus = "excellent" | "good" | "needs attention" | "sick" | "critical";
7
8// Dinosaur types
9type TaxonomicGroup = "theropods" | "sauropods" | "ceratopsians" | "stegosaurs" | "ankylosaurs";
10
11type DietClass = "herbivore" | "carnivore" | "omnivore";
12
13type SpeciesData = {
14  name: string;
15  taxonomicGroup: TaxonomicGroup;
16  diet: DietClass;
17  geologicalEra: string;
18  averageWeight: number;
19  averageLength: number;
20  averageAge: number;
21  threat: AccessLevel;
22  description: string;
23};
24
25type Specimen = {
26  id: ID;
27  species: string;
28  firstName: string;
29  age: number;
30  weight: number;
31  gender: "male" | "female";
32  status: HealthStatus;
33  location: ID;
34  arrivalDate: Date;
35  history: MedicalHistory[];
36};
37
38type MedicalHistory = {
39  date: Date;
40  type: "examination" | "treatment" | "vaccination" | "surgery";
41  description: string;
42  veterinarian: ID;
43};
44
45// Employee types
46type Position =
47  | "director"
48  | "researcher"
49  | "veterinarian"
50  | "trainer"
51  | "security"
52  | "maintenance"
53  | "technician"
54  | "guide";
55
56type Employee = {
57  id: ID;
58  firstName: string;
59  lastName: string;
60  position: Position;
61  accessLevel: AccessLevel;
62  hireDate: Date;
63  contact: {
64    phone: string;
65    email: string;
66    address?: string;
67  };
68  specializationAreas?: string[];
69};
70
71// Location types
72type LocationType =
73  | "enclosure"
74  | "laboratory"
75  | "operations center"
76  | "public zone"
77  | "dining"
78  | "shop"
79  | "attraction";
80
81type SecuritySystems = {
82  fence: boolean;
83  videoMonitoring: boolean;
84  motionSensors: boolean;
85  backupPower: boolean;
86  electricVoltage?: number; // only for predator enclosures
87};
88
89type Location = {
90  id: ID;
91  name: string;
92  type: LocationType;
93  coordinates: Coordinates;
94  area: number; // in hectares
95  capacity: number; // maximum number of people/specimens
96  requiredAccessLevel: AccessLevel;
97  status: ActivityStatus;
98  securitySystems: SecuritySystems;
99};
100
101// Event types
102type EventType =
103  | "dinosaurEscape"
104  | "systemFailure"
105  | "accident"
106  | "illness"
107  | "birth"
108  | "death"
109  | "specimenTransfer"
110  | "weatherThreat";
111
112type Event = {
113  id: ID;
114  type: EventType;
115  date: Date;
116  location: ID;
117  dinosaurParticipants?: ID[];
118  employeeParticipants?: ID[];
119  description: string;
120  status: "in progress" | "resolved" | "closed";
121  priority: 1 | 2 | 3 | 4 | 5;
122  preventiveActions?: string[];
123};
124
125// Park management system
126type ManagementSystem = {
127  dinosaurs: Record<ID, Specimen>;
128  species: Record<string, SpeciesData>;
129  employees: Record<ID, Employee>;
130  locations: Record<ID, Location>;
131  events: Record<ID, Event>;
132
133  // System methods
134  addSpecimen: (specimen: Omit<Specimen, "id">) => ID;
135  updateSpecimenStatus: (id: ID, status: HealthStatus) => void;
136  moveSpecimen: (id: ID, newLocation: ID) => boolean;
137  findSpecimens: (filters: Partial<Specimen>) => Specimen[];
138  generateStatusReport: () => StatusReport;
139};
140
141type StatusReport = {
142  specimenCount: number;
143  speciesDistribution: Record<string, number>;
144  healthStatuses: Record<HealthStatus, number>;
145  locationOccupancy: Record<ID, { capacity: number; current: number }>;
146  recentEvents: Event[];
147  alerts: string[];
148};
149
150// Simulating part of the system
151const jurassicParkSystem: Partial<ManagementSystem> = {
152  dinosaurs: {},
153  species: {
154    "Tyrannosaurus": {
155      name: "Tyrannosaurus Rex",
156      taxonomicGroup: "theropods",
157      diet: "carnivore",
158      geologicalEra: "late Cretaceous",
159      averageWeight: 7000,
160      averageLength: 12,
161      averageAge: 30,
162      threat: 5,
163      description: "One of the largest predators that ever walked the Earth."
164    },
165    "Velociraptor": {
166      name: "Velociraptor",
167      taxonomicGroup: "theropods",
168      diet: "carnivore",
169      geologicalEra: "late Cretaceous",
170      averageWeight: 15,
171      averageLength: 1.8,
172      averageAge: 25,
173      threat: 4,
174      description: "Small, fast and intelligent predator, hunting in packs."
175    }
176  },
177  locations: {
178    "LOC-001": {
179      id: "LOC-001",
180      name: "T-Rex Paddock",
181      type: "enclosure",
182      coordinates: [20.1234, -75.8765],
183      area: 8,
184      capacity: 2,
185      requiredAccessLevel: 4,
186      status: "active",
187      securitySystems: {
188        fence: true,
189        videoMonitoring: true,
190        motionSensors: true,
191        backupPower: true,
192        electricVoltage: 10000
193      }
194    }
195  }
196};
197
198// Function demonstrating usage of the types
199function demonstrateManagementSystem() {
200  console.log("Jurassic Park Management System");
201  console.log("----------------------------------");
202
203  console.log("Available species:");
204  Object.values(jurassicParkSystem.species || {}).forEach(species => {
205    console.log(`- ${species.name} (${species.diet}, threat: ${species.threat})`);
206  });
207
208  console.log("\nAvailable locations:");
209  Object.values(jurassicParkSystem.locations || {}).forEach(loc => {
210    console.log(`- ${loc.name} (type: ${loc.type}, status: ${loc.status}, access: level ${loc.requiredAccessLevel})`);
211  });
212}
213
214demonstrateManagementSystem();

Summary

Type aliases in TypeScript are a powerful mechanism that allows the creation of more readable, maintainable and safe code. In the context of managing a complex system like Jurassic Park, type aliases allow us to accurately model data and the relationships between them.

| Type alias feature | Use in Jurassic Park | |-------------------|----------------------| | Basic aliases | Types of identifiers, security levels | | Complex type aliases | Data structures for dinosaurs, employees, locations | | Generic aliases | Monitoring system for different parameters | | Recursive types | Species hierarchies, organizational structure | | Discriminated unions | Different data levels, types of events | | Utility types | Managing data mutability and accessibility |

Dr. Wu summarizes: "Type aliases are like our checklists and protocols in the laboratory - they allow us to clearly define what data and operations should look like, while making the whole system more understandable. In a project as complex as Jurassic Park, where the smallest error can lead to catastrophic consequences, such precise tools are invaluable."

Go to CodeWorlds