We use cookies to enhance your experience on the site
CodeWorlds

Parameter and return value types

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.

Basics of function typing

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.

Optional and default parameters

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.

Return value types

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

Functions returning void

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

Functions returning different types (union types)

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.

Function Types

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

Typing functions with callbacks

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.

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

Function Overloading

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.

Typing async functions

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.

Summary

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

Go to CodeWorlds