Welcome back to Jurassic Park! After many excursions through interfaces and types, Dr. Henry Wu will take us today to the park's operations center, where we will look more closely at function typing - that is, specifying the types of parameters and return values. This is a key element of ensuring safety and predictability in our park management system - just as strict security protocols are essential for the smooth operation of Jurassic Park.
Specifying types for parameters and return values is a fundamental element of TypeScript. This allows us to clearly define what data a function accepts and what it returns - creating a clear "contract" for that function.
1// Basic function with typed parameters and return value
2function obliczWymaganiaPokarmowe(
3 waga: number,
4 jestPredatoriem: boolean,
5 wiek: number
6): number {
7 // Predatory dinosaurs need more calories
8 const dietCoefficient = jestPredatoriem ? 0.05 : 0.03;
9
10 // Young dinosaurs need more calories per unit of mass
11 const ageCoefficient = wiek < 5 ? 1.2 : 1.0;
12
13 // Daily food amount in kilograms
14 return waga * dietCoefficient * ageCoefficient;
15}
16
17// Using the function
18const trexDailyMeal = obliczWymaganiaPokarmowe(7500, true, 7);
19console.log(`Daily food amount for T-Rex: ${trexDailyMeal} kg`);
20
21const brachiosaurusDailyMeal = obliczWymaganiaPokarmowe(35000, false, 22);
22console.log(`Daily food amount for Brachiosaurus: ${brachiosaurusDailyMeal} kg`);The function above clearly defines that it takes three parameters:
waga (number), jestPredatoriem (boolean) and wiek (number), and returns a value of type number. Thanks to this, the IDE and TypeScript compiler will be able to catch potential errors, such as attempting to call the function with incorrect argument types.In Jurassic Park, some procedures have optional parameters or default values. For example, when scheduling a dinosaur examination, we can specify the priority level, but if we don't, a standard value is assumed.
1// Function with optional parameter
2function zaplanujBadanieDinosaura(
3 id: string,
4 typ: "routine" | "specialist",
5 priorytet?: 1 | 2 | 3 // Optional parameter
6): string {
7 // If priority was not specified, we use a default value
8 const faktycznyPriorytet = priorytet || 2;
9
10 // Determining the examination date based on priority
11 let terminWDniach: number;
12 switch (faktycznyPriorytet) {
13 case 1: terminWDniach = 1; break; // Emergency - next day
14 case 2: terminWDniach = 7; break; // Standard priority - one week
15 case 3: terminWDniach = 30; break; // Low priority - one month
16 default: terminWDniach = 7;
17 }
18
19 // Calculating the examination date
20 const dataBadania = new Date();
21 dataBadania.setDate(dataBadania.getDate() + terminWDniach);
22
23 return `${typ} examination for dinosaur ${id} scheduled for ${dataBadania.toLocaleDateString()} (priority: ${faktycznyPriorytet})`;
24}
25
26// Calling with all parameters
27console.log(zaplanujBadanieDinosaura("TREX-01", "specialist", 1));
28
29// Calling without the optional parameter
30console.log(zaplanujBadanieDinosaura("STEG-03", "routine"));
31
32// Alternatively, you can use a parameter with a default value
33function zaplanujBadanieDinosaura2(
34 id: string,
35 typ: "routine" | "specialist",
36 priorytet: 1 | 2 | 3 = 2 // Parameter with default value
37): string {
38 const dataBadania = new Date();
39 dataBadania.setDate(dataBadania.getDate() + (priorytet === 1 ? 1 : priorytet === 2 ? 7 : 30));
40
41 return `${typ} examination for dinosaur ${id} scheduled for ${dataBadania.toLocaleDateString()} (priority: ${priorytet})`;
42}
43
44// Calling without providing the parameter with a default value
45console.log(zaplanujBadanieDinosaura2("TRIC-02", "routine"));Notice the difference between an optional parameter (denoted by
?) and a parameter with a default value (specified by = value). In both cases we can omit this parameter when calling the function, but the default value approach is more elegant and readable.In function typing, return value types are just as important as parameter types. TypeScript allows us to precisely specify what exactly a function returns, which helps detect potential errors.
1// Function returning a number
2function calculateEnclosureCapacity(
3 length: number,
4 width: number,
5 typDinosaura: "small" | "medium" | "large"
6): number {
7 const powierzchnia = length * width;
8
9 // How many dinosaurs of a given type can be safely placed in a given area
10 switch (typDinosaura) {
11 case "small": return Math.floor(powierzchnia / 50);
12 case "medium": return Math.floor(powierzchnia / 200);
13 case "large": return Math.floor(powierzchnia / 1000);
14 default: return 0;
15 }
16}
17
18// Function returning string
19function generujIdentyfikatorDinosaura(
20 species: string,
21 numer: number
22): string {
23 // Creating an identifier based on the first 4 letters of the species and the number
24 const prefiks = species.slice(0, 4).toUpperCase();
25 // Formatting the number as 3-digit with leading zeros
26 const numerSformatowany = numer.toString().padStart(3, '0');
27
28 return `${prefiks}-${numerSformatowany}`;
29}
30
31// Function returning boolean
32function czyWybiegBezpieczny(
33 securityLevel: number,
34 wymaganyPoziom: number,
35 lastReview: Date
36): boolean {
37 // Check if security level is sufficient
38 if (securityLevel < wymaganyPoziom) {
39 return false;
40 }
41
42 // Check if the inspection was within the last 30 days
43 const dzisiaj = new Date();
44 const thirtyDaysAgo = new Date(dzisiaj);
45 thirtyDaysAgo.setDate(dzisiaj.getDate() - 30);
46
47 return lastReview >= thirtyDaysAgo;
48}
49
50// Usage examples
51const raptorEnclosureCapacity = calculateEnclosureCapacity(100, 50, "medium");
52console.log(`Raptor enclosure can hold ${raptorEnclosureCapacity} individuals`);
53
54const idNowegoDinosaura = generujIdentyfikatorDinosaura("Velociraptor", 42);
55console.log(`New raptor received ID: ${idNowegoDinosaura}`);
56
57const statusWybiegTRex = czyWybiegBezpieczny(
58 5, // current level
59 5, // required level
60 new Date(2023, 4, 15) // date of last inspection (May 15, 2023)
61);
62console.log(`T-Rex enclosure safety status: ${statusWybiegTRex ? "SAFE" : "NEEDS ATTENTION"}`);Some functions return no value - they are called mainly for their side effects, such as modifying system state, displaying information or saving data. In TypeScript we denote this case with return type
void.1// Function returning no value (void)
2function zarejestrujZmianyOgrodzenia(
3 idWybiegu: string,
4 voltage: number,
5 dataZmiany: Date = new Date() // By default we use the current date
6): void {
7 // Saving information about the fence voltage change
8 console.log(`[${dataZmiany.toISOString()}] Voltage in enclosure ${idWybiegu} fence changed to ${voltage}V`);
9
10 // In a real application we would save this data in the database
11 // The function returns no value
12}
13
14// Calling the void function
15zarejestrujZmianyOgrodzenia("TRX-PADDOCK", 10000);In some cases a function may return different types of values depending on conditions or parameters. In TypeScript we can specify this using union types.
1// Function returning different types of values
2function pobierzDaneDinosaura(
3 id: string,
4 formatDanych: "summary" | "full" | "JSON"
5): string | object | null {
6 // Simulating a database
7 const bazaDanych: Record<string, {
8 nazwa: string;
9 species: string;
10 wiek: number;
11 waga: number;
12 statusZdrowia: string;
13 }> = {
14 "TREX-001": {
15 nazwa: "Rexy",
16 species: "Tyrannosaurus Rex",
17 wiek: 7,
18 waga: 8000,
19 statusZdrowia: "good"
20 },
21 "VEL-001": {
22 nazwa: "Blue",
23 species: "Velociraptor",
24 wiek: 4,
25 waga: 160,
26 statusZdrowia: "excellent"
27 }
28 };
29
30 // Check if the dinosaur exists
31 if (!bazaDanych[id]) {
32 console.error(`Dinosaur with ID ${id} not found`);
33 return null;
34 }
35
36 const dino = bazaDanych[id];
37
38 // Return different formats depending on the parameter
39 switch (formatDanych) {
40 case "summary":
41 return `${dino.nazwa} (${dino.species}): ${dino.statusZdrowia}`;
42
43 case "full":
44 return `
45 ID: ${id}
46 Name: ${dino.nazwa}
47 Species: ${dino.species}
48 Age: ${dino.wiek} years
49 Weight: ${dino.waga} kg
50 Health status: ${dino.statusZdrowia}
51 `;
52
53 case "JSON":
54 return { id, ...dino };
55
56 default:
57 return null;
58 }
59}
60
61// Usage examples
62const podsumowanieRexy = pobierzDaneDinosaura("TREX-001", "summary");
63console.log(podsumowanieRexy);
64
65const fullBlueData = pobierzDaneDinosaura("VEL-001", "full");
66console.log(fullBlueData);
67
68const jsonDaneRexy = pobierzDaneDinosaura("TREX-001", "JSON") as object;
69console.log(JSON.stringify(jsonDaneRexy, null, 2));
70
71// Handling missing data
72const nonExistentDinosaur = pobierzDaneDinosaura("INDOMINUS-001", "summary");
73if (nonExistentDinosaur === null) {
74 console.log("Dinosaur not found");
75}The
pobierzDaneDinosaura function returns string, object or null depending on the parameters and conditions. Thanks to union types TypeScript knows that the result can be one of these three types and verifies the code that uses this function accordingly.TypeScript also allows defining the types of functions themselves, which is extremely useful when passing functions as parameters or assigning them to variables.
1// Defining a function type
2type FunkcjaAlarmowa = (
3 threatLevel: number,
4 lokalizacja: string,
5 komunikat: string
6) => void;
7
8// Function using a function as a parameter
9function runSecurityProcedure(
10 idWybiegu: string,
11 threatLevel: number,
12 funkcjaAlarmowa: FunkcjaAlarmowa
13): void {
14 console.log(`Activating security procedure for enclosure ${idWybiegu}`);
15
16 // Closing gates
17 console.log(`- Closing gates of enclosure ${idWybiegu}`);
18
19 // Activating additional security measures
20 if (threatLevel >= 4) {
21 console.log("- Activating additional electric fence");
22 console.log("- Dispatching security personnel");
23 }
24
25 // Calling the passed alarm function
26 funkcjaAlarmowa(
27 threatLevel,
28 `Enclosure ${idWybiegu}`,
29 `Security procedure level ${threatLevel} activated`
30 );
31}
32
33// Implementation of an alarm function
34const alertDlaCentrumKontroli: FunkcjaAlarmowa = (poziom, lokalizacja, komunikat) => {
35 console.log("=== ALERT FOR CONTROL CENTER ===");
36 console.log(`Threat level: ${poziom}`);
37 console.log(`Location: ${lokalizacja}`);
38 console.log(`Message: ${komunikat}`);
39 console.log("=================================");
40};
41
42// Alternative alarm function
43const alertDlaPersoneluParku: FunkcjaAlarmowa = (poziom, lokalizacja, komunikat) => {
44 const priorytet = poziom >= 4 ? "CRITICAL" : poziom >= 2 ? "IMPORTANT" : "INFORMATIONAL";
45 console.log(`[${priorytet}] ${komunikat} at ${lokalizacja}`);
46};
47
48// Using the function with different alarm function implementations
49runSecurityProcedure("RAPTOR-01", 3, alertDlaCentrumKontroli);
50runSecurityProcedure("TREX-01", 5, alertDlaPersoneluParku);Functions with callbacks are common in JavaScript, especially in asynchronous operations. TypeScript allows precise typing of such functions.
1// Function with inline-typed callback
2function monitorujZdrowieAnimalcia(
3 idAnimalcia: string,
4 intervalInSeconds: number,
5 callback: (dane: { temperatura: number; heartRate: number; poziomStresu: number }) => void
6): void {
7 console.log(`Health monitoring started for animal ${idAnimalcia} every ${intervalInSeconds} seconds`);
8
9 // Simulating the first measurement
10 const initialData = {
11 temperatura: 38.5,
12 heartRate: 72,
13 poziomStresu: 3
14 };
15
16 // Calling the callback with the first data
17 callback(initialData);
18
19 console.log("Monitoring started");
20}
21
22// Using the function with a callback
23monitorujZdrowieAnimalcia("TREX-001", 60, (dane) => {
24 console.log("New health data received:");
25 console.log(`- Temperature: ${dane.temperatura}°C`);
26 console.log(`- Heart rate: ${dane.heartRate} beats/min`);
27 console.log(`- Stress level: ${dane.poziomStresu}/10`);
28
29 // Checking health alerts
30 if (dane.temperatura > 39.5) {
31 console.log("ALERT: Elevated temperature!");
32 }
33
34 if (dane.heartRate > 100) {
35 console.log("ALERT: Elevated heart rate!");
36 }
37
38 if (dane.poziomStresu > 7) {
39 console.log("ALERT: High stress level!");
40 }
41});
42
43// You can also define the callback type separately
44type CallbackMonitoringuZdrowia = (dane: {
45 temperatura: number;
46 heartRate: number;
47 poziomStresu: number;
48 timeStamp?: Date; // optional parameter
49}) => void;
50
51// Function with a callback type
52function zaawansowanyMonitoringZdrowia(
53 idAnimalcia: string,
54 opcje: { interval: number; powiadomienia: boolean },
55 callback: CallbackMonitoringuZdrowia
56): void {
57 console.log(`Configuring advanced monitoring for ${idAnimalcia}`);
58 console.log(`- Interval: ${opcje.interval} seconds`);
59 console.log(`- Notifications: ${opcje.powiadomienia ? "enabled" : "disabled"}`);
60
61 // Simulating data with timestamp
62 const dane = {
63 temperatura: 38.2,
64 heartRate: 68,
65 poziomStresu: 2,
66 timeStamp: new Date()
67 };
68
69 callback(dane);
70}
71
72// Using the function with callback type
73zaawansowanyMonitoringZdrowia("STEG-003", { interval: 120, powiadomienia: true }, (dane) => {
74 const czasPomiaru = dane.timeStamp ? dane.timeStamp.toISOString() : "unknown";
75 console.log(`[${czasPomiaru}] Health data: T:${dane.temperatura}°C, HR:${dane.heartRate}, Stress:${dane.poziomStresu}/10`);
76});In the above example we defined a function type
FunkcjaAlarmowa, which specifies the signature of a function taking three parameters and returning no value. We then used this type to define a parameter in the runSecurityProcedure function and implementations of specific alarm functions.One of the most powerful features of TypeScript are generics, which allow creating functions and types that work with different data types while maintaining type safety.
1// Generic function for filtering data
2function filtrujDane<T>(
3 dane: T[],
4 predykat: (element: T) => boolean
5): T[] {
6 return dane.filter(predykat);
7}
8
9// Sample dinosaur data
10interface DaneDino {
11 id: string;
12 nazwa: string;
13 species: string;
14 waga: number;
15 dangerLevel: number;
16}
17
18const dinosaury: DaneDino[] = [
19 { id: "TREX-001", nazwa: "Rexy", species: "T-Rex", waga: 8000, dangerLevel: 5 },
20 { id: "VEL-001", nazwa: "Blue", species: "Velociraptor", waga: 150, dangerLevel: 4 },
21 { id: "TRIC-001", nazwa: "Tri", species: "Triceratops", waga: 9000, dangerLevel: 2 },
22 { id: "STEG-001", nazwa: "Spike", species: "Stegosaurus", waga: 5000, dangerLevel: 2 },
23 { id: "BRACH-001", nazwa: "Brachie", species: "Brachiosaurus", waga: 40000, dangerLevel: 1 }
24];
25
26// Using the generic function with type DaneDino
27const niebezpieczneDinosaury = filtrujDane<DaneDino>(
28 dinosaury,
29 dino => dino.dangerLevel >= 4
30);
31
32console.log("Dangerous dinosaurs:");
33niebezpieczneDinosaury.forEach(dino => {
34 console.log(`- ${dino.nazwa} (${dino.species}): threat level ${dino.dangerLevel}`);
35});
36
37// Using the same function with a different data type
38const temperatury = [28.5, 29.2, 30.1, 27.8, 31.5, 30.8, 32.1, 29.5];
39const wysokieTemperatury = filtrujDane<number>(
40 temperatury,
41 temp => temp > 30
42);
43
44console.log(`Recorded ${wysokieTemperatury.length} measurements with temperature above 30°C`);
45
46// More complex generic function for data operations
47function processAndReturnData<T, U>(
48 dane: T[],
49 transformacja: (element: T) => U
50): U[] {
51 return dane.map(transformacja);
52}
53
54// Using the complex generic function
55const raportDinosaurs = processAndReturnData<DaneDino, { nazwa: string; threatLevel: string }>(
56 dinosaury,
57 dino => ({
58 nazwa: `${dino.nazwa} (${dino.species})`,
59 threatLevel: dino.dangerLevel >= 4
60 ? "HIGH"
61 : dino.dangerLevel >= 3
62 ? "MEDIUM"
63 : "LOW"
64 })
65);
66
67console.log("Threat report:");
68raportDinosaurs.forEach(raport => {
69 console.log(`- ${raport.nazwa}: ${raport.threatLevel}`);
70});Generic functions are extremely flexible and allow reusing the same logic with different data types while maintaining type safety.
TypeScript enables function overloading, which allows defining multiple function signatures with different parameter types and return values.
1// Function overloading for different ID formats
2// Function signature definitions
3function generujIdentyfikator(typ: "dinosaur", numer: number): string;
4function generujIdentyfikator(typ: "employee", firstName: string, nazwisko: string): string;
5function generujIdentyfikator(typ: "location", strefa: string, numer: number): string;
6
7// Function implementation handling all cases
8function generujIdentyfikator(
9 typ: "dinosaur" | "employee" | "location",
10 value1: number | string,
11 value2?: string
12): string {
13 switch (typ) {
14 case "dinosaur":
15 return `DINO-${(value1 as number).toString().padStart(3, '0')}`;
16
17 case "employee":
18 if (typeof value1 === "string" && typeof value2 === "string") {
19 const initials = `${value1.charAt(0)}${value2.charAt(0)}`.toUpperCase();
20 return `EMP-${initials}-${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`;
21 }
22 throw new Error("Invalid parameters for type 'employee'");
23
24 case "location":
25 if (typeof value1 === "string" && typeof value2 === "number") {
26 return `LOC-${value1.toUpperCase()}-${value2.toString().padStart(2, '0')}`;
27 }
28 throw new Error("Invalid parameters for type 'location'");
29
30 default:
31 throw new Error(`Unknown identifier type: ${typ}`);
32 }
33}
34
35// Usage examples for different overloads
36const idDinosaura = generujIdentyfikator("dinosaur", 42);
37console.log(`Dinosaur ID: ${idDinosaura}`);
38
39const idPracownika = generujIdentyfikator("employee", "John", "Hammond");
40console.log(`Employee ID: ${idPracownika}`);
41
42const idLokalizacji = generujIdentyfikator("location", "A", 5);
43console.log(`Location ID: ${idLokalizacji}`);Function overloading allows creating more flexible and readable API interfaces, where the same function can handle different sets of parameters.
In modern JavaScript we often use asynchronous functions. TypeScript works well with Promises and the
async/await keywords.1// Async function returning Promise<string>
2async function pobierzDaneDinosauraZAPI(id: string): Promise<string> {
3 console.log(`Fetching dinosaur data ${id} from API...`);
4
5 // Simulating network delay
6 await new Promise(resolve => setTimeout(resolve, 1000));
7
8 // Simulating data from API
9 const dinosaury: Record<string, { nazwa: string; species: string }> = {
10 "TREX-001": { nazwa: "Rexy", species: "Tyrannosaurus Rex" },
11 "VEL-001": { nazwa: "Blue", species: "Velociraptor" }
12 };
13
14 if (dinosaury[id]) {
15 const dino = dinosaury[id];
16 return `${dino.nazwa} (${dino.species})`;
17 } else {
18 throw new Error(`Dinosaur with ID ${id} not found`);
19 }
20}
21
22// Async function returning Promise<boolean>
23async function checkHealthStatus(id: string): Promise<boolean> {
24 console.log(`Checking health status of dinosaur ${id}...`);
25
26 try {
27 // Simulating delay
28 await new Promise(resolve => setTimeout(resolve, 800));
29
30 // Simulating result (for simplicity always returns true)
31 return true;
32 } catch (error) {
33 console.error(`Error while checking health status: ${error}`);
34 return false;
35 }
36}
37
38// Async function using multiple async operations
39async function przygotujRaportDinosaura(id: string): Promise<{
40 id: string;
41 dane: string;
42 statusZdrowia: boolean;
43 czasGenerowania: string;
44}> {
45 console.log(`Preparing report for dinosaur ${id}...`);
46
47 // Parallel execution of two async operations
48 const [dane, statusZdrowia] = await Promise.all([
49 pobierzDaneDinosauraZAPI(id),
50 checkHealthStatus(id)
51 ]);
52
53 return {
54 id,
55 dane,
56 statusZdrowia,
57 czasGenerowania: new Date().toISOString()
58 };
59}
60
61// Using async functions
62async function asyncUsageExample() {
63 try {
64 const raport = await przygotujRaportDinosaura("TREX-001");
65 console.log("Report ready:");
66 console.log(JSON.stringify(raport, null, 2));
67
68 // Attempting to fetch a non-existent dinosaur
69 const nonExistent = await pobierzDaneDinosauraZAPI("INDOMINUS-001")
70 .catch(error => {
71 console.error(`An error occurred: ${error.message}`);
72 return "Unknown dinosaur";
73 });
74
75 console.log(`Result for non-existent: ${nonExistent}`);
76 } catch (error) {
77 console.error(`An error occurred: ${error}`);
78 }
79}
80
81asyncUsageExample();In the above example we defined several asynchronous functions returning different types of promises (Promise<string>, Promise<boolean> and Promise<object>). TypeScript provides full type support for these functions and allows safe chaining of asynchronous operations.
Typing function parameters and return values is one of the fundamental elements of TypeScript that helps create safe, predictable and maintainable code. In the context of our Jurassic Park, good function typing is like solid security protocols - they help prevent potential catastrophes before they happen.
| Technique | Use in Jurassic Park | |-----------|----------------------| | Basic function typing | Functions calculating dinosaur food requirements | | Optional and default parameters | Scheduling dinosaur examinations with different priorities | | Void functions | Registering changes in security systems | | Union types in return values | Fetching dinosaur data in different formats | | Function types | Defining alarm functions for security procedures | | Callbacks | Monitoring dinosaur health | | Generic functions | Filtering and processing different types of data | | Function overloading | Generating different types of identifiers | | Async functions | Fetching data from API and generating reports |
Dr. Wu summarizes: "In Jurassic Park every procedure must be precisely defined and executed to ensure the safety of both guests and our precious specimens. Similarly in TypeScript, proper function typing ensures that every operation proceeds exactly as planned - without surprises. And as we all know, in the world of dinosaurs, surprises are rarely pleasant."