03 Types and Data Representation
A practical guide to JavaScript types, conversions, type checks, strings, and choosing data representations that make programs clear and reliable.
1 - Understand Values and References
A data type describes what kind of value a program is using and which operations are valid for that value. Numbers support arithmetic, strings support text operations, and Booleans support logical decisions. Choosing a representation that matches the meaning of the data improves readability, testing, and maintenance.
JavaScript is dynamically typed: variables do not need declared types, and one variable can hold values of different types at different times. This flexibility makes deliberate type handling important.
Primitive and reference behavior
A is immutable. For example, calling toUpperCase() on a string returns a new string; it does not change the original string unless the result is assigned back to the variable.
A is an -based value accessed through a reference. After const second = first, both variables can refer to the same , so changing second.score can also change the value observed through first. To avoid this sharing for top-level properties, create a shallow copy such as { ...original }. Remember that nested objects and arrays may still be shared.
Assigning a primitive creates an independent value. For example, after let a = 10; let b = a; b = 20, the value of a remains 10. By contrast, assigning an variable generally copies the reference, not every property.
Takeaway: Distinguish between creating a new and mutating an reached through a shared reference.
2 - Convert and Compare Values
changes a value from one type to another. Prefer explicit conversion when the intended result matters. Common operations include Number("42"), parseFloat("3.14"), String(42), and Boolean(1).
Conversion can fail. Number("not a number") produces NaN, a special numeric value that indicates an unsuccessful numeric result. Use Number.isNaN(value) to test for it; comparing directly with NaN does not work because NaN === NaN is false.
JavaScript also performs implicit coercion. The + operator adds numbers but concatenates when either operand is a string. For example, 2 + 3 produces 5, while "2" + 3 and 2 + "3" produce "23".
This is why text input must be converted before arithmetic. If quantity and price both contain numeric text, quantity + price joins the text, whereas Number(quantity) + Number(price) performs numeric addition.
Use with === and strict inequality with !== in most programs. These operators avoid ordinary during comparison. The looser == and != operators permit conversion and can produce matches that are less obvious.
Takeaway: Convert external text explicitly, check failed conversions, and compare values with the expected types.
3 - Check Values Safely
Type checking helps a program decide whether a value can safely be used. The returns strings such as "number", "string", "boolean", "undefined", and "bigint".
There are two important limitations:
typeof nullreturns""for historical reasons, so check null directly withvalue === null.typeofreturns""for arrays and ordinary objects alike.
Use .isArray(value) to distinguish an from another . Use instanceof when the ’s constructor or prototype-chain identity matters, as in names instanceof .
In general validation, check the operations or properties the program actually needs, not only the constructor category. Also handle null, undefined, and NaN deliberately because each represents a different condition.
Takeaway: Combine basic type checks with special-case checks and validation of the data your code actually requires.
4 - Work with Strings
A string is a sequence of text characters. JavaScript strings are immutable and use zero-based indexing. For example, if const message = "Hello", then message[0] is "H", message.length is 5, and message.includes("ell") is true.
Common methods return new strings, including toUpperCase(), toLowerCase(), slice(), startsWith(), and replace(). Calling a method does not modify the original string unless the returned value is assigned.
Template literals combine text with expressions clearly. For example, const sentence = \You ordered {product}s.`` can combine a quantity and product name in one readable string.
Use strings for textual data and established serialized formats. Avoid packing several unrelated fields into an improvised delimiter-based string such as "Ava|42|active" when an can name each field explicitly. A structured makes the roles of name, score, and status visible.
Takeaway: Treat strings as immutable text, and use structured representations when the data has multiple meaningful fields.
5 - Choose Useful Data Representations
Choose a representation according to the operations the program needs:
Use a number for quantities, measurements, indexes, and calculations.
Use a Boolean for a true-or-false condition rather than arbitrary strings such as
"yes"or"no".Use an for an ordered sequence.
Use an for a record with named fields.
Use a
Setwhen values must be unique.Use a
Mapwhen key-value lookup is central and keys may have varied types.Use a string for text or for serialized data handled by an established format.
An is appropriate when position and order matter. An is appropriate when each value has a named role. A Set stores unique values, while a Map stores key-value associations and permits keys of many types.
Ordinary JavaScript numbers use floating-point representation, so some decimal calculations can contain small rounding errors. For example, 0.1 + 0.2 can produce 0.30000000000000004. Applications requiring exact decimal arithmetic need an appropriate decimal strategy rather than assuming every decimal calculation is exact.
For data crossing a program boundary, is a common choice. .stringify() converts an in-memory structure to text, and .parse() reconstructs a JavaScript value from that text. Parsing reconstructs a value but does not prove that the data has the expected shape. Validate external values before using their properties.
Takeaway: Let the meaning and required operations determine the representation, then validate data at every external boundary.
6 - Apply Type-Safety Habits
Reliable type handling comes from a small set of consistent habits:
Convert input explicitly because user input and many external sources arrive as strings.
Prefer
===and!==unless conversion during comparison is intentional.Handle
null,undefined, andNaNdeliberately.Use arrays for sequences and objects for named records.
Avoid unnecessary mutation when references may be shared.
Keep collection elements consistent whenever possible.
Validate values from users, files, APIs, and storage before relying on them.
These habits connect a value’s meaning, its representation, and the operations performed on it. They also make assumptions visible: input is converted at boundaries, comparisons use known types, and data structures are selected for the work they must perform.
Takeaway: Clear type handling is a combination of explicit conversion, deliberate checking, suitable representations, and careful control of shared state.