DEV Community

Bethany Jones
Bethany Jones

Posted on

Understanding JavaScript Data Types (Through the Lens of D&D & Other TTRPGs)

Humans love to make sense of our world and ourselves through categorization; whether it's understanding biological life through taxonomy, ourselves through identity labels, or even our fictional RPG characters through their "classes," we want to break things down into their smallest components and group them based on certain characteristics and similarities. Computer programming, and JavaScript in particular, is no different.

Image description

What Are Data Types & Why We Need Them
Just as the Dungeon & Dragons RPG system has a set of rules that governs how the different character classes behave, programming languages have a type system that classifies how the built-in data structures should behave depending on their "type."

As Aaron Krauss describes it in his blog "Programming Concepts: Static Vs. Dynamic Type Checking":

A type system is a collection of rules that assign a property called type to various constructs in a computer program, such as variables, expressions, functions or modules, with the end goal of reducing the number of bugs by verifying that data is represented properly throughout a program.

In other words, data types refers to the 'type' of value we're working with. It's a way to categorize the information/data that we use so that we interact with our code appropriately.

Every value is associated with a certain data type that falls into one of these *main categories:

  • Number
  • String
  • Boolean
  • Array
  • Object
  • Function
  • undefined
  • null
  • NaN
let partyMembers = 9; // Number
let partyName = 'The Fellowship'; // String
let allSurvive = false; // Boolean

// Array
let partyMemberAges = [87, 38, 2000, 50, 2000, 28, 36, 139, 40]; 

// Object
let partyClasses = {
Aragorn: 'Fighter',
Sam: 'Paladin',
Gandalf: 'Wizard',
Frodo: 'NPC',
Legolas: 'Fighter',
Pippin: 'Rogue',
Merry: 'Bard',
Gimli: 'Fighter',
Boromir: 'Fighter'

function averagePartyAge(array){
let totalAge = 0;
for(var i = 0; i < array.length; i++){
totalAge += array[i];
return totalAge / array.length;

let bestMember; // undefined
let worstMember = null; // null
let middleEarth = partyName * allSurvive; // NaN
Enter fullscreen mode Exit fullscreen mode

These data types have various properties and methods associated with them that allow the JavaScript interpreter to correctly handle this data. For example, the interpreter treats numbers differently from strings, so while 2 + 2 and '2' + '2' look very similar, they are actually different data types: Numbers and Strings, respectively. Without understanding their type, the interpreter would not be able to execute this code correctly. So, now that you know why we need these data types, let's talk about each of the data types in turn.

The Number data type represents all numeric values (both positive and negative). Categorizing these numeric values as Numbers allows us to use the static properties and methods attached to that data type. We are also able to perform calculations on Number data types by using arithmetic operations.

let currentXP = 6; // resolves to the number 6
let previousXP = 1000; // resolves to 1000
let totalXP = currentXP + previousXP; // resolves to 1006
Enter fullscreen mode Exit fullscreen mode

currentXP and previousXP are Number data types because they have a numeric value and are not written as strings, which means we are able to add these values together.

The String data type is a collection of characters between either single or double quotes. Strings allow us to represent data in text form.

let moreExperience = 'Your character gets 500 more xp!'; 
let xpUpdate = '500'; // resolves to a string of '500' 
Enter fullscreen mode Exit fullscreen mode

The variables moreExperience and xpUpdate are strings, despite also having numbers in them. If you were to try and add xpUpdate + currentXP, the JavaScript interpreter would type coerce currentXP as string rather than adding the values together to create a sum. So, instead of outputting 506 as you might expect, it would concatenate these values as a string and return 5006.

Arithmetic operations cannot be used on this variable unless it is first converted to the number data type.

Booleans hold only one of two values: true or false. Booleans are most useful when storing values with clear binaries, such as yes/no or on/off. When only one of two values are possible, Booleans are usually used.

let haveSpellSlots = true;
let spellsCount = 0;
if(spellsCount <= 0){ 
haveSpellSlots = false; // change to a Boolean of false
console.log(haveSpellSlots); // prints => false
Enter fullscreen mode Exit fullscreen mode

The variable haveSpellSlots is assigned to the Boolean true, denoting that yes, we have spell slots available to use. If the value of spellsCount is less than or equal to 0, however, the haveSpellSlots is reassigned to a Boolean of false.

The array data type categorizes objects that can store a collection of items in an indexed list. These data types can be expanded, and thus are also considered complex data types. Arrays are denoted by the use of [brackets] when assigned a variable.

let characterTraits = ['1', 5, [], true, 'Adora', 'Druid']; 
console.log(typeof(characterTraits)); // prints => 'object'
console.log(Array.isArray(characterTraits)); // prints => true
Enter fullscreen mode Exit fullscreen mode

The variable characterTraits is assigned to an array containing 6 elements.

The object data type is used for storing a collection of keyed data and other data types (like functions). Essentially objects are variables that can store a multitude of values. Like arrays, objects are also considered a complex data type.

const dndCharacter = {
    class: 'Druid',
    level: 1,
    cantrips: ['Guidance', 'Mending', 'Shillelagh'],
    lvlSpells: ['Cure Wounds', 'Healing Word'],
    haveSpells: false,
Enter fullscreen mode Exit fullscreen mode

The above dndCharacter object has keys of class, level, cantrips, lvlSpells, and haveSpells, each of these containing different types of values.

The function data types denote a block of statements that perform some task or calculation. Functions are a complex data type.

function whoIsDM(name){
console.log(`${name} is the Dungeon Master`);

whoIsDM('Chris'); // calls the function

'Chris is the Dungeon Master' // logged to the console
Enter fullscreen mode Exit fullscreen mode

The undefined data type represents undefined values or values that have not been initialized.

let nextCampaign; 
console.log(nextCampaign); // prints => undefined
Enter fullscreen mode Exit fullscreen mode

The nextCampaign variable is declared, but has not been assigned to any value, thus when it is later called, it returns undefined.

The null data type represents none or the absence of value. These occur when the programmer intentionally initializes a piece of data to null.

let empty = null; // assigned a variable to null
console.log(empty); // prints => null
Enter fullscreen mode Exit fullscreen mode

NaN is a special number type that stands for Not-A-Number. NaN happens when an arithmetic operation is applied to a invalid or undefined value.

console.log('Do not split the party!' / 9); // prints => NaN 
Enter fullscreen mode Exit fullscreen mode

Primitive vs. Complex Data Types
In addition to the above categories, cata can can also be classified as either primitive/simple or complex data types. We can use the typeof operator to help us determine what type a piece of data is.

Primitive/Simple data types include: Numbers, Strings, Booleans, NaNs, undefined, and null. These data types are considered simple because they are atomic and immutable, which means they do not aggregate other values (like arrays and objects).

Also, simple data types are copy by value meaning that any code that acts on these data types would simply be working with a copy of the original, rather than modifying the original value itself.

So, manipulating a primitive data type would result in an entirely new simple value. Variables can hold the actual values of primitive types, but this necessitates that simple data types be a fixed size.

Complex data types include: Objects, Arrays, and Functions. Unlike simple data types, complex data types can collect and add to the values contained within. These data types are store as a reference, meaning that we can act directly upon the original, thus changing it. Because complex data types can be added to and manipulated, they have an indefinite size.

let characterName = 'Adora'; 
let druidName = 'Adora';  
Enter fullscreen mode Exit fullscreen mode

The variable characterName is a simple data type with a string. The value 'Adora' cannot be mutated or altered directly. druidName is an entirely new variable with a new value with no relationship to the above variable. Because strings are immutable, you cannot use methods to alter the original. You can see this demonstrated below.

druidName.toUpperCase(); // nothing happens
console.log(druidName); // prints => 'Adora'
druidName = 'Adora Mossweather'; // reassigned
console.log(druidName); // prints => 'Adora Mossweather'
Enter fullscreen mode Exit fullscreen mode

Nothing happens to druidName as it cannot be directly altered with these methods. However, variables can be reassigned, as shown by the variable druidName which has taken on a new value.

Unlike Strings, Numbers, and Booleans, Objects can be mutated by using an object method on the original. For example:

const dndCharacter = {
    class: 'Druid',
    level: 1,
    cantrips: ['Guidance', 'Mending', 'Shillelagh'],
    lvlSpells: ['Cure Wounds', 'Healing Word'],
    haveSpells: false,
} = druidName; // adds druidName property 
console.log(dndCharacter); // prints => updated object
Enter fullscreen mode Exit fullscreen mode

Because dndCharacter is an object, we are able to include a new property called name with the druidName as its value.

Copy by Value vs Copy by Reference
All primitive data values are copied by value, while all complex data types are copied by reference. What that means is a variable can reference another variable, but the value stored is merely a replica of the original, as demonstrated below.

characterName = druidName; // points to a copy of druidName
console.log(characterName); // prints => 'Adora Mossweather'

characterName = 'Adoria Mossy'; // reassigns to a new value

console.log(druidName); // prints => 'Adora Mossweather'
this variable was not affected by the above variable's reassignment

console.log(characterName); // prints => 'Adoria Mossy'
Enter fullscreen mode Exit fullscreen mode

The druidName variable was not affected by the characterName variable's reassignment.

Complex data types, however, are copied by reference which means that the original object, array, or function can be modified directly.

dndCharacter.lvlSpells.push('Barkskin', 'Flaming Sphere', 'Moonbeam'); 
// adds 3 more elements to the spells array located in the dndCharacter object

console.log(dndCharacter); // prints => object with the values

  class: 'Druid',
  level: 1,
  cantrips: [ 'Guidance', 'Mending', 'Shillelagh' ],
  lvlSpells: [
    'Cure Wounds',
    'Healing Word',
    'Flaming Sphere',
  haveSpells: false,
  name: 'Adora Mossweather'

let dndParty = dndCharacter;
console.log(dndParty); // prints => same object as dndCharacter

dndParty.companions = ['Percy', 'Roark', 'Vox']; 
console.log(dndCharacter.companions); // prints => array

dndParty = {};
console.log(dndParty); // prints => {}
Enter fullscreen mode Exit fullscreen mode

The variable dndParty points directly to the same object as the variable dndCharacter. Because of copy by reference, when we add a new key property to dndParty, dndCharacter is updated as well. It is only when we reassign dndParty to a new empty object that it is no longer referencing the object stored at dndCharacter.

And, that, in a nutshell is what JavaScript data types are and how you can identify them in your own code.

*BigInt and Symbols are also a data type, but we will not be touching on those in this article.

Top comments (0)