JavaScript and TypeScript course Β· Module 1: JavaScript Basics

Type Conversion and Operations on Types

15 min read
In this lesson8

In Jurassic Park, scientists often need to transform data from one form to another - translating DNA sequences into dinosaur attributes, converting measurements between different systems, or analyzing animal behavior. Similarly in JavaScript, type conversion and operations on data types are fundamental skills that allow for effective data manipulation.

Implicit Type Conversion (Type Coercion)

JavaScript is dynamically typed (values have types, variables do not) and weakly typed: when an operation needs it, it converts values to another type on its own. It's like the automatic adaptive systems in Jurassic Park that adjust to changing conditions - sometimes convenient, but sometimes surprising.

Conversion in Arithmetic Operations

When JavaScript performs arithmetic operations, it tries to automatically convert values to numbers:

1// String + Number -> String (concatenation)
2"Velociraptor " + 3;  // "Velociraptor 3"
3
4// String - Number -> Number
5"50" - 10;  // 40 (string "50" is converted to a number)
6
7// String * Number -> Number
8"5" * 4;    // 20 (string "5" is converted to a number)
9
10// String / Number -> Number
11"20" / 5;   // 4 (string "20" is converted to a number)

Notice the special case of the + operator, which for strings acts as a concatenation (joining) operator, not addition. It's like observing that velociraptors always hunt in packs - knowledge that can save your life!

Conversion in Comparison Operations

When comparing values of different types using the == (loose equality) operator, JavaScript tries to convert them to a common type:

1// Number == String
25 == "5";     // true (string "5" is converted to number 5)
3
4// Boolean == Number
5true == 1;    // true (true is converted to 1)
6false == 0;   // true (false is converted to 0)
7
8// null == undefined
9null == undefined;  // true (special case in the specification)
10
11// Object == Primitive
12[1] == 1;     // true ([1] is converted to "1", then to 1)

Such behavior resembles "intuitive" observations in the park - "This enclosure looks the same as that one" - which can be misleading if you don't know the details.

Conversion in Logical Operations

Logical operators &&, || and ! can work with values of any type, implicitly converting them to boolean:

1// The ! (negation) operator converts a value to boolean and negates it
2!0;           // true (0 is converted to false, then negated)
3!"Dino";      // false ("Dino" is converted to true, then negated)
4
5// The && and || operators can return original values, not just booleans
6"T-Rex" && "Raptor";  // "Raptor" (if the first operand is truthy, returns the second)
70 || "Backup";        // "Backup" (if the first operand is falsy, returns the second)

This logic is like the protocol in the park: "If the fence is working AND it's tall enough, the dinosaur won't escape" - both conditions must be met for the entire statement to be true.

Explicit Type Conversion

To avoid unexpected results from implicit conversion, it's often better to explicitly convert values to the desired type. It's like precise laboratory protocols in Jurassic Park - exactly defined procedures yielding predictable results.

Conversion to String

1// Method #1: String() function
2String(42);         // "42"
3String(true);       // "true"
4String(null);       // "null"
5String(undefined);  // "undefined"
6String({ name: "T-Rex" });  // "[object Object]"
7String([1, 2, 3]);  // "1,2,3"
8
9// Method #2: toString() method
10(42).toString();    // "42"
11true.toString();    // "true"
12[1, 2, 3].toString();  // "1,2,3"
13// null.toString();    // Error! Null and undefined don't have the toString() method
14
15// Method #3: Concatenation with an empty string
1642 + "";            // "42"
17true + "";          // "true"
18null + "";          // "null"

Conversion to Number

1// Method #1: Number() function
2Number("42");       // 42
3Number("42px");     // NaN (cannot convert the entire string to a number)
4Number(true);       // 1
5Number(false);      // 0
6Number(null);       // 0
7Number(undefined);  // NaN
8Number([1]);        // 1 (single-element array is converted to its element)
9Number([1, 2]);     // NaN (multi-element array cannot be converted)
10
11// Method #2: parseInt() and parseFloat() functions
12parseInt("42");     // 42
13parseInt("42px");   // 42 (converts until it encounters an invalid character)
14parseInt("3.14");   // 3 (converts only the integer part)
15parseFloat("3.14"); // 3.14 (converts the floating-point part)
16
17// Method #3: Unary + and - operators
18+"42";              // 42
19-"42";              // -42
20+true;              // 1

Conversion to Boolean

1// Method #1: Boolean() function
2Boolean(42);        // true
3Boolean(0);         // false
4Boolean("");        // false
5Boolean("text");    // true
6Boolean(null);      // false
7Boolean(undefined); // false
8Boolean({});        // true (every object is truthy)
9
10// Method #2: Double negation !!
11!!42;               // true
12!!0;                // false
13!!"text";           // true
14!!null;             // false

Conversion Tables

The following tables are like dinosaur genetic charts in the park - they show exactly what happens during conversion between different types.

Conversion to String

Input TypeInput ExampleString Conversion Result
Number42"42"
Booleantrue"true"
nullnull"null"
undefinedundefined"undefined"
Array[1, 2, 3]"1,2,3"
Object{name: "Rex"}"[object Object]"
Functionfunction(){}"function(){}"

Conversion to Number

Input TypeInput ExampleNumber Conversion Result
String"42"42
String" 42 "42 (whitespace is removed)
String"42px"NaN
String""0 (empty string)
Booleantrue1
Booleanfalse0
nullnull0
undefinedundefinedNaN
Array[]0 (empty array)
Array[5]5 (single-element array)
Array[1, 2]NaN (multiple elements)
Object{}NaN

Conversion to Boolean

Input TypeInput ExampleBoolean Conversion Result
Number42true
Number0false
NumberNaNfalse
String"text"true
String""false
nullnullfalse
undefinedundefinedfalse
Object{} (any)true
Array[] (any)true

Operations on Data Types

Now let's look at detailed operations on individual data types. It's like specialized procedures for different dinosaur species.

String Operations

Strings in JavaScript offer a rich set of methods for text manipulation:

1const parkName = "Jurassic Park";
2
3// String length
4parkName.length;  // 13
5
6// Accessing individual characters
7parkName[0];        // "J"
8parkName.charAt(0); // "J" (alternative method)
9
10// Searching for substrings
11parkName.indexOf("Park");      // 9 (index where "Park" begins)
12parkName.startsWith("Jur");    // true
13parkName.endsWith("Park");     // true
14parkName.includes("assic");    // true
15
16// Changing case
17parkName.toUpperCase();        // "JURASSIC PARK"
18parkName.toLowerCase();        // "jurassic park"
19
20// Extracting fragments
21parkName.substring(0, 8);      // "Jurassic"
22parkName.slice(-4);            // "Park" (counts from the end)
23
24// Replacing fragments
25parkName.replace("Jurassic", "Dinosaur");  // "Dinosaur Park"
26
27// Splitting a string into an array
28parkName.split(" ");           // ["Jurassic", "Park"]
29
30// Removing whitespace
31"  Danger Zone  ".trim();      // "Danger Zone"
32
33// Joining strings
34"Welcome".concat(" to ", parkName); // "Welcome to Jurassic Park"
35
36// Repeating a string
37"Roar! ".repeat(3);            // "Roar! Roar! Roar! "

Number Operations

JavaScript offers many possibilities for working with numbers, both directly on values and through the Math object:

1// Basic arithmetic operations
25 + 3;    // 8 (addition)
35 - 3;    // 2 (subtraction)
45 * 3;    // 15 (multiplication)
55 / 3;    // 1.6666666666666667 (division)
65 % 3;    // 2 (modulo - remainder of division)
75 ** 3;   // 125 (exponentiation - 5 to the power of 3)
8
9// Increment and decrement
10let dinoCount = 5;
11dinoCount++;  // dinoCount is now 6
12dinoCount--;  // dinoCount is back to 5
13
14// Assignment and operation operators
15dinoCount += 3;  // Same as: dinoCount = dinoCount + 3
16dinoCount *= 2;  // Same as: dinoCount = dinoCount * 2
17
18// Math object for advanced operations
19Math.abs(-5);            // 5 (absolute value)
20Math.round(4.7);         // 5 (rounding to the nearest integer)
21Math.ceil(4.1);          // 5 (rounding up)
22Math.floor(4.9);         // 4 (rounding down)
23Math.max(3, 7, 2, 4);    // 7 (finding the maximum)
24Math.min(3, 7, 2, 4);    // 2 (finding the minimum)
25Math.pow(2, 3);          // 8 (exponentiation - 2 to the power of 3)
26Math.sqrt(16);           // 4 (square root)
27Math.sin(0);             // 0 (sine)
28Math.cos(0);             // 1 (cosine)
29Math.random();           // Random number from 0 (inclusive) to 1 (exclusive)
30
31// Generating a random number within a range
32function getRandom(min, max) {
33    return Math.floor(Math.random() * (max - min + 1)) + min;
34}
35getRandom(1, 10);  // Random number from 1 to 10
36
37// Formatting numbers
38const height = 5.678934;
39height.toFixed(2);           // "5.68" (two decimal places, returns a string)
40height.toPrecision(3);       // "5.68" (three significant digits, returns a string)
41height.toString(2);          // "101.1010110111001110100111100101111000100100011110001" (binary representation)
42(1234567).toLocaleString();  // "1,234,567" (formatting with separators, depends on locale)

Boolean Operations

Although the boolean type has only two possible values, there are many logical operations:

1// Comparison operators
25 > 3;              // true (greater than)
35 >= 5;             // true (greater than or equal)
43 < 5;              // true (less than)
53 <= 3;             // true (less than or equal)
65 == "5";           // true (loose equality - different types are converted)
75 === "5";          // false (strict equality - types must match)
85 != "6";           // true (loose inequality)
95 !== "5";          // true (strict inequality)
10
11// Logical operators
12true && true;       // true (logical AND - both conditions must be true)
13true && false;      // false
14true || false;      // true (logical OR - at least one condition must be true)
15false || false;     // false
16!true;              // false (logical NOT - negation)
17!false;             // true
18
19// Complex conditions
20const isSafe = fenceActive && powerLevel > 5000 && !stormApproaching;
21const needsEvacuation = alarmTriggered || dinoEscaped || stormStrength > 8;

Operations on null and undefined

Despite the limited possibilities for direct operations on null and undefined, there are useful patterns for working with them:

1// The || operator for default values (note: it also replaces 0 and an empty string)
2const visitorName = userName || "Guest";  // Use userName if it exists, otherwise "Guest"
3
4// Nullish coalescing operator (??) - available in newer JS versions
5const count = dinoCount ?? 0;  // Use dinoCount if not null/undefined, otherwise 0
6
7// Optional chaining (?.) - available in newer JS versions
8const species = dinosaur?.species;  // Safely access the property, even if dinosaur is null/undefined

Interesting and Tricky Type Conversion Cases

Some type conversions in JavaScript can be surprising, similar to unusual dinosaur behavior after a storm:

1// Strange cases of loose equality (==)
2[] == '';            // true (empty array is converted to empty string)
3[1] == 1;            // true (array [1] is converted to string "1", then to number 1)
4[1, 2] == '1,2';     // true (array [1, 2] is converted to string "1,2")
5true == '1';         // true (true is converted to 1, and '1' to 1)
6false == '0';        // true (false is converted to 0, and '0' to 0)
7null == undefined;   // true (special case in the specification)
8'' == 0;             // true (empty string is converted to 0)
9
10// Operations with NaN
11NaN == NaN;          // false (NaN is not equal to anything, not even itself)
12Number.isNaN(NaN);   // true (the proper way to check for NaN)
13
14// Operations with objects
15const obj1 = {}, obj2 = {};
16obj1 == obj2;        // false (objects are compared by reference, not content)
17obj1 === obj2;       // false (objects are compared by reference, not content)
18
19// Operations on arrays
20[] + [];             // "" (arrays are converted to strings, then joined)
21[] + {};             // "[object Object]" (object is converted to string)
22{} + [];             // 0 (empty {} is treated as a code block, and +[] is converted to a number)

Best Practices

To avoid surprises when working with data types, it's worth following these rules - they're like safety protocols in Jurassic Park:

1. Use strict comparison (===, !==)

1// BAD
2if (dinoCount == "5") {  // Will work, but can lead to bugs
3
4// GOOD
5if (dinoCount === 5) {  // Clearly indicates that dinoCount should be a number

2. Explicitly convert types when needed

1// BAD
2const totalVisitors = visitorCount + newVisitors;  // May give incorrect results if either variable is a string
3
4// GOOD
5const totalVisitors = Number(visitorCount) + Number(newVisitors);  // Explicit conversion to numbers

3. Use appropriate type-checking methods

1// BAD
2if (typeof height == "number" && !isNaN(height)) {  // Works, but is imprecise
3
4// GOOD
5function isValidNumber(value) {
6    return typeof value === 'number' && !isNaN(value) && isFinite(value);
7}
8if (isValidNumber(height)) {

4. Be careful with object operations

1// BAD
2if (dinosaur == otherDinosaur) {  // Compares references, not content
3
4// GOOD
5function isEqual(obj1, obj2) {
6    return JSON.stringify(obj1) === JSON.stringify(obj2);  // For simple objects
7}
8if (isEqual(dinosaur, otherDinosaur)) {

5. Use nullish coalescing (??) and optional chaining (?.) operators

1// INSTEAD OF
2const safeVisitorCount = visitorCount !== null && visitorCount !== undefined ? visitorCount : 0;
3const speciesName = dinosaur && dinosaur.species ? dinosaur.species : "Unknown";
4
5// BETTER (in newer JS)
6const safeVisitorCount = visitorCount ?? 0;
7const speciesName = dinosaur?.species ?? "Unknown";

Practical Example: Processing Dinosaur Data

The following example shows how to apply type operations in a practical scenario:

1// Data coming from various sources (API simulation, forms, CSV files)
2const rawDinoData = [
3    { id: "1", name: "Rexy", species: "T-Rex", height: "5.6", dangerous: "true", lastFed: "2023-05-15T08:30:00" },
4    { id: 2, name: "Blue", species: "Velociraptor", height: "1.8", dangerous: true, lastFed: "2023-05-15T09:15:00" },
5    { id: "3", name: "Spike", species: "Stegosaurus", height: 4.2, dangerous: "false", lastFed: null },
6    { id: "4", name: "Cera", species: "Triceratops", height: "unknown", dangerous: false, lastFed: "invalid-date" }
7];
8
9// Function to normalize and validate data
10function processDinosaurData(dinoList) {
11    // Prepare the normalized array
12    const processedData = [];
13
14    // Process each element
15    for (let i = 0; i < dinoList.length; i++) {
16        const dino = dinoList[i];
17
18        // Normalize ID
19        const id = Number(dino.id);
20        if (isNaN(id) || id <= 0) {
21            console.warn(`Skipped dinosaur with invalid ID: ${dino.id}`);
22            continue;
23        }
24
25        // Normalize name
26        const name = String(dino.name).trim();
27        if (name === "") {
28            console.warn(`Skipped dinosaur without a name, ID: ${id}`);
29            continue;
30        }
31
32        // Normalize species
33        const species = String(dino.species).trim() || "Unknown";
34
35        // Normalize height
36        let height;
37        if (dino.height === "unknown" || dino.height === undefined) {
38            height = null;
39        } else {
40            height = Number(dino.height);
41            if (isNaN(height)) {
42                console.warn(`Invalid height for ${name}, set to null`);
43                height = null;
44            }
45        }
46
47        // Normalize dangerous flag
48        let dangerous;
49        if (typeof dino.dangerous === 'string') {
50            dangerous = dino.dangerous.toLowerCase() === 'true';
51        } else {
52            dangerous = Boolean(dino.dangerous);
53        }
54
55        // Normalize last feeding date
56        let lastFed = null;
57        if (dino.lastFed) {
58            const date = new Date(dino.lastFed);
59            if (!isNaN(date.getTime())) {
60                lastFed = date;
61            } else {
62                console.warn(`Invalid feeding date for ${name}`);
63            }
64        }
65
66        // Additional calculations
67        const securityLevel = dangerous ? (height > 3 ? "High" : "Medium") : "Low";
68        const daysFromLastFeed = lastFed ? Math.floor((new Date() - lastFed) / (1000 * 60 * 60 * 24)) : null;
69
70        // Add the normalized dinosaur to the result array
71        processedData.push({
72            id,
73            name,
74            species,
75            height,
76            dangerous,
77            lastFed,
78            securityLevel,
79            daysFromLastFeed,
80            needsFeeding: daysFromLastFeed === null || daysFromLastFeed > 1
81        });
82    }
83
84    return processedData;
85}
86
87// Using the function
88const normalizedDinos = processDinosaurData(rawDinoData);
89console.log(normalizedDinos);
90
91// Additional data processing
92const dangerousDinos = normalizedDinos.filter(dino => dino.dangerous);
93const withHeight = normalizedDinos.filter(dino => dino.height !== null);
94const avgHeight = withHeight.reduce((sum, dino) => sum + dino.height, 0) / withHeight.length;
95const dinosBySecurityLevel = normalizedDinos.reduce((groups, dino) => {
96    groups[dino.securityLevel] = groups[dino.securityLevel] || [];
97    groups[dino.securityLevel].push(dino);
98    return groups;
99}, {});

This example shows how to handle different data types, convert them, validate them, and normalize them - all fundamental operations that Jurassic Park scientists would need to perform on daily data.

Summary

Type conversion and operations on data types in JavaScript is a broad topic that requires understanding many nuances. The key aspects are:

  1. Implicit conversion - JavaScript automatically converts types in many contexts

    • Can lead to unexpected results
    • Is convenient but sometimes dangerous
  2. Explicit conversion - Controlled type transformation

    • String(), Number(), Boolean() - conversion functions
    • toString(), parseInt(), parseFloat() - specialized methods and functions
    • !! - quick conversion to boolean
  3. Operations on individual types

    • Strings have a rich set of methods for text manipulation
    • Numbers can be processed using operators and the Math object
    • Boolean values are the foundation for conditional statements
    • Null and undefined have special rules in operations
  4. Best practices

    • Using strict comparison (===, !==)
    • Explicitly converting types where needed
    • Using appropriate checking functions

Just as in Jurassic Park, where different dinosaur species require different handling protocols, in JavaScript different data types require the appropriate approach. Knowledge of conversion rules and type operations enables effective and safe data manipulation - the foundation of every JavaScript application.

In the next lesson, we will look at arithmetic and assignment operators, which allow for more advanced calculations - essential for managing resources in such a complex undertaking as Jurassic Park.

Code for this lesson: index.js
1// Type conversion and type operations
2// Experiment with implicit and explicit type conversion in JavaScript
3
4// === Implicit conversion (Type Coercion) ===
5console.log("=== Implicit conversion ===");
6
7// String + Number -> concatenation
8console.log("Dinosaurs: " + 5); // "Dinosaurs: 5"
9console.log("5" + 3);            // "53" (string!)
10
11// String - Number -> Number
12console.log("10" - 3);   // 7
13console.log("5" * 2);    // 10
14
15// Boolean in operations
16console.log(true + 1);   // 2
17console.log(false + 1);  // 1
18
19// === Explicit conversion ===
20console.log("\n=== Explicit conversion ===");
21
22// To Number
23console.log(Number("42"));       // 42
24console.log(Number("abc"));      // NaN
25console.log(parseInt("100px"));  // 100
26console.log(parseFloat("3.14m")); // 3.14
27
28// To String
29console.log(String(42));         // "42"
30console.log(String(true));       // "true"
31console.log((255).toString(16)); // "ff" (hex)
32
33// To Boolean
34console.log(Boolean(0));         // false
35console.log(Boolean(""));        // false
36console.log(Boolean("text"));    // true
37console.log(Boolean(1));         // true
38
39// === typeof operator ===
40console.log("\n=== typeof ===");
41console.log(typeof 42);          // "number"
42console.log(typeof "text");      // "string"
43console.log(typeof true);        // "boolean"
44console.log(typeof undefined);   // "undefined"
45console.log(typeof null);        // "object" (historical bug!)
46
47// Your task:
48// 1. Check the result "5" + 3 vs "5" - 3
49// 2. Use Number() to convert a string to a number
50// 3. Check typeof for different values
51

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What will the expression Number('hello') return in JavaScript?

  2. 2. What will the expression '5' - 2 return in JavaScript?

  3. 3. Which method of explicit string-to-number conversion is INCORRECT?

  4. 4. Which of the following values is NOT a falsy value in JavaScript?

Hands-on tasks in the game

  • Horizontal ordering

    Arrange the template literal elements:

Useful articles