18: Exploring TypeScript: PrimitiveTypes

A deep dive into 7 primitives

1/14/20256 min read

TypeScript 3D logo on it's back - blog title - wip-podcast.com
TypeScript 3D logo on it's back - blog title - wip-podcast.com

This is part of a semi-monthly series that will put TypeScript under a microscope to become more adept overall. 🔬

Understanding the nitty gritty bits and pieces of a language can only benefit us as software builders!

This post will cover basic primitive types within TypeScript.

  1. 🤔 What are primitive types?

  2. 📚 Resources for further reading

Let's dive in!

You’re probably thinking, “Why are we going so low-level and foundational?”

Now that we’ve covered the TypeScript Compiler and Runtime, primitive types are a natural next step in continuing to build upon a strong foundation. The stronger the base knowledge, the better we can grow toward more advanced topics.

So, let’s take it from the top!

🤔 What are primitive types?

To start, we need a shared understanding of the types we will discuss. Because TypeScript is a superset of JavaScript, these types will naturally mirror some JavaScript.

A few differences exist between JavaScript primitives and TypeScript primitives, mainly relating to the types themselves because TS is a superset of JS. The TypeScript documentation is your best source if you have questions about TypeScript primitives!

What is a “primitive” in the sense of programming?

Consider a primitive as a very low-level and built-in aspect of a language. It describes the specific data type of one specific value. They are also what the built-in typeof operator might return in some cases.

These types are immutable. They can be assigned to a variable, and a variable can have its value reassigned, but that does not change the type of the initial value itself.

Let’s take a look at the primitives that span across both JavaScript and TypeScript:

  • string

  • number

  • boolean

  • bigint

  • symbol

  • null and undefined

As a note, you may notice that these types are referred to in lowercase (string, number, boolean). If you see them referred to in uppercase (String, Number, Boolean), these refer to the built-in types that contain the methods and properties of the built-in objects for these types. You can learn more from MDN!

🧵 string

The string type is one of the three most common primitives. It represents data in the form of text. In TypeScript, you can declare the string type using the string keyword, as you might expect:

You’ll notice that single quotes are used in my above example (‘). Double quotes (“) may also be used, as well as backticks (`) for a template literal. These are the same options as in JavaScript, just with the added type declaration. Here are examples using these last two options together:

If you weren’t aware, TypeScript is also smart enough to infer types! However, this can come with confusion. In a function, TypeScript would understand that the output will be a string:

However, if you were to use const variable declaration for a string without declaring it as a string more broadly, TypeScript may infer this as a type literal! Take a look at this example. The variable test is the literal string of “Hello” as its type, not a more generic string!

TypeScript type literal in the VSCode IDE
TypeScript type literal in the VSCode IDE

Why is this? When we use const in this way, we essentially make the variable read-only. The variables are unable to be reassigned and, therefore, remain constant. If we cannot reassign test to, say, “Goodbye” then it makes some sense that its type would be “Hello” as the type literal as opposed to a string.

Although it seems rather simple, I have found nuance is littered throughout TypeScript. Let’s keep this in mind as we examine the remaining primitive types.

When in doubt, hover your mouse in the IDE to check it out! 👀

#️⃣ number

Another of the three most common primitive types is number. As you may have assumed, this type represents number data! These can be integers or floating-point numbers that may or may not contain decimals.

As with the string type, you can declare a variable as a number with the corresponding keyword:

As with strings, TypeScript will attempt to infer the type if you do not state it:

As you can see from the variable zero, using const will again cause TypeScript to infer a type literal of 0 as opposed to the more broad number type.

When in doubt, check it out! 👀

Do you want more straight to your inbox?

Subscribe to receive occassional blog posts!
Your contact information will never be sold.

⚖️ boolean

The boolean type is the third most common primitive. This refers to the two logical values of true and false.

They are typically used in conditional testing like if…else or while statements that return a truth or false result. You can also use the ternary operator; here’s an example:

And, if you were wondering, a type literal will occur if using const as with the number and string primitives.

When in doubt, check it out! 👀

🏋 bigint

The bigint primitive is similar to number, but the value is too large to be represented by a typical number.

There are subtle differences between number and bigint. First, the built-in Math objects cannot be used on a bigint value. Additionally, bigint values cannot mix with number values in operations. These number types must be coerced to the same type first. Coercion to a number value, however, can cause degraded bigint precision.

Overall, this primitive is rarely used in my own experience. My work and side projects thus far haven’t dealt with such large numbers! If you have a great real-world example, I’d love to hear more about it. 🤓

For now, here’s a simple example:

Again, similar to the previous primitives, using const will result in a type literal:

When in doubt, check it out! 👀

👾 symbol

Another primitive that I rarely use is a symbol. If you have a great example from your own experience, please share! 🤓

A symbol is an immutable and exclusive value for a property key created using the Symbol() constructor. Strings are optional to provide a key value used to access a symbol at a later time.

This is the only primitive with a reference identity making it unique. In some ways, they behave like objects. Often, a symbol can be used to add a unique property to an object that acts as a hidden mechanism from other code that might typically access a key.

Each symbol creation is completely separate. Let’s take a look:

In addition, TypeScript has the concept of a symbol subtype - called unique symbol which allows a symbol to be treated as a unique literal from explicit type annotations.

Similar to the type literals in the above primitives, const can be used to declare a unique symbol or we can use a combination of readonly and static properties. To access or reference the unique symbol, the typeof operator should be used.

Let’s look at an example that my friend GitHub Copilot helped generate using const:

When in doubt, check it out! 👀

🍽️ null and undefined

These primitives I use frequently! Both null and undefined express a lack of value, but there are subtle differences. To better explore them, I opted to describe null and undefined together.

Let’s start with null. This means that a variable was defined and null explicitly assigned to express the absence of a value. This value may be intentional or to express that there is no known value yet to apply.

Here’s a quick couple of examples:

Moving on to undefined. This means a variable was declared, but not defined or a value was not assigned. The undefined assignment to the value happens automatically if you do not initialize that variable.

Let’s take a look at a quick example:

What are the more subtle differences then?

First, null represents an intentional absence of a value, whereas, undefined typically indicates an unintentional absence of a value. Of course, there are cases to use both intentionally, but null has to be assigned while undefined is automatically assigned when a value is not initiated.

That can be a little confusing, so let’s take a look at an example of undefined where you might use it intentionally. Let’s say we’re trying to apply grade scores to a respective letter grade, but maybe there can be a glitch in the system providing the grade scores.

Perhaps we can add some checks for such an undefined incident and handle it appropriately now.

The next difference is that null represents the absence of an object. In our earlier example, we expected a result to be an object containing details about a name record. On the other hand, undefined is a lack of any value at all.

Finally, equality between the two differs. null and undefined are loosely equal (null == undefined), but not strictly equal (null !== undefined). Loose equality performs type coercion and both primitives evaluate to an absence of value. Strict equality, however, checks whether the data types and values are the same and null and undefined are ultimately not the same data types.

When in doubt, always check it out! 👀

I hope that covering these primitives can be at least a little bit informative! In writing this I learned a few new things myself or at least considered aspects of these types I hadn’t before. 🤓

📚 Resources for further reading

Thanks so much for reading!

Did I miss anything, or would you like to add anything?

Let me know!

I appreciate constructive feedback so we can all learn together. 🙌