TypeScript Basic Types: Understanding Primitive and Built-in Types

July 25, 2025

☕️ Support Us
Your support will help us to continue to provide quality content.👉 Buy Me a Coffee

You've got TypeScript installed from the last article and you've seen that : string syntax in action. But maybe you're wondering: "I've been writing JavaScript for years without worrying about types. Why should I start now?"

Imagine we're building a price calculator for ExplainThis subscriptions. A user selects a plan and adds some extras, and we need to calculate the total cost. But here's the kind of bug that could easily slip through:

function calculateTotalPrice(planPrice, extraFeatures) {
  return planPrice + extraFeatures;
}

// Plan price comes from a form input (string), extra features from JavaScript (number)
const totalPrice = calculateTotalPrice("29", 15);
console.log(totalPrice); // "2915" - wait, what?

That moment when you realize your "$29" string just got concatenated with a number instead of doing math? Instead of a $44 total, you get "$2915" - definitely not what your users expect to pay! That's exactly the kind of bug TypeScript's type system prevents. Let's see how.

When JavaScript Gets Confused

JavaScript tries to be helpful by automatically converting types, but this often backfires. It will attempt to make sense of whatever you throw at it, even when the result isn't what you intended. The price calculator example above shows how JavaScript's automatic type conversion can cause unexpected bugs.

// These all "work" in JavaScript, but probably not how you expect
"5" - 3; // 2 (string becomes number)
"5" + 3; // "53" (number becomes string)
true + 1; // 2 (boolean becomes number)
null + 5; // 5 (null becomes 0)

TypeScript takes a different approach. Instead of guessing what you meant, it asks you to be explicit about what types of data your code expects to work with. This might feel like extra work at first, but it catches these mixing mistakes before your code even runs.

Your First TypeScript Types

Let's fix that reading time calculator with TypeScript's most fundamental types. These are the building blocks you'll use constantly:

function calculateReadingTime(
  wordCount: number,
  wordsPerMinute: number
): number {
  return Math.ceil(wordCount / wordsPerMinute);
}

const readingTime = calculateReadingTime("500", 200); // Error: Argument of type 'string' is not assignable to parameter of type 'number'

TypeScript just saved you from that string concatenation bug. The moment you try to pass a string where a number is expected, you get a helpful error message.

The three types we just encountered (number, string, and the return type number) are part of TypeScript's primitive types. Let's look at all of them:

// The three primitive types you'll use most often
let articleTitle: string = "TypeScript Basics";
let viewCount: number = 1250;
let isPublished: boolean = true;

// TypeScript prevents type mismatches
articleTitle = 42; // Error: Type 'number' is not assignable to type 'string'
viewCount = "one thousand"; // Error: Type 'string' is not assignable to type 'number'
isPublished = "yes"; // Error: Type 'string' is not assignable to type 'boolean'

These types work exactly like you'd expect from JavaScript, but with safety guardrails. A string holds text, a number holds any numeric value (integers or decimals), and a boolean is either true or false.

Working with Collections

Individual values are useful, but most real applications work with collections of data. TypeScript extends the primitive types to handle arrays and objects in a type-safe way.

Arrays in TypeScript specify what type of elements they contain:

const articleViews: number[] = [1250, 892, 2100, 456];
const categories: string[] = ["TypeScript", "JavaScript", "React"];
const publishedFlags: boolean[] = [true, false, true];

// TypeScript prevents you from mixing types accidentally
articleViews.push("trending"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
categories.push(100); // Error: Argument of type 'number' is not assignable to parameter of type 'string'

The number[] syntax means "an array of numbers." TypeScript ensures that everything you add to that array is actually a number. Same goes for string[] and boolean[].

Objects work similarly, but you define the shape of the object: what properties it should have and what types those properties should be:

const article: { title: string; wordCount: number; isPublished: boolean } = {
  title: "TypeScript Basic Types",
  wordCount: 2500,
  isPublished: true,
};

// TypeScript checks that you provide all required properties
const incompleteArticle: { title: string; wordCount: number } = {
  title: "React Hooks Guide",
  // Error: Property 'wordCount' is missing
};

// And prevents typos in property names
console.log(article.titel); // Error: Property 'titel' does not exist. Did you mean 'title'?

This object type syntax might look verbose, but it catches so many common bugs. Typos in property names, missing properties, wrong value types - TypeScript catches them all before your code runs.

When TypeScript Figures It Out for You

Something that might surprise you: you don't always need to write out these type annotations. TypeScript is often smart enough to figure out what type something should be based on the value you assign to it.

// TypeScript infers these types automatically
let welcomeMessage = "Welcome to ExplainThis!"; // TypeScript knows this is string
let totalArticles = 42; // TypeScript knows this is number
let siteIsLive = false; // TypeScript knows this is boolean

// You can verify this by trying to assign wrong types
welcomeMessage = 123; // Error: Type 'number' is not assignable to type 'string'
totalArticles = "forty-two"; // Error: Type 'string' is not assignable to type 'number'

This feature is called type inference, and it's one of TypeScript's most helpful aspects. You get type safety without having to annotate every single variable.

Arrays and simple objects benefit from inference too:

const articleIds = [1, 2, 3, 4]; // TypeScript infers number[]
const mixed = ["TypeScript", 42, true]; // TypeScript infers (string | number | boolean)[]
const author = { name: "Alice", articlesWritten: 30 }; // TypeScript infers { name: string; articlesWritten: number }

But there's a catch. TypeScript can only infer types when it can see the initial value. Function parameters are a perfect example of where you need to be explicit:

// TypeScript can't guess what types you'll pass to a function, so it requires explicit annotations
function greetReader(name: string, articlesRead: number): string {
  return `Hello ${name}, you've read ${articlesRead} articles on ExplainThis!`;
}

// But it can infer the return type based on what you return, so no annotation needed here
function calculateTotalViews(todayViews: number, yesterdayViews: number) {
  return todayViews + yesterdayViews; // TypeScript infers this returns number - no need to write ': number' here
}

Think of it this way: TypeScript can see what you're doing inside a function, but it can't predict what arguments you'll pass to it from the outside.

Handling Missing Values

Real applications often deal with data that might not exist. A user might not have provided their phone number, or an API call might return null instead of data. TypeScript has specific types for these situations:

let authorBio: string | null = null; // Might be a string or null - tells TypeScript this variable can be empty
let socialHandle: string | undefined = undefined; // Might be a string or undefined - another way to signal a value might not exist

// TypeScript forces you to handle null cases - without the null check, you'd get an error
function sendNewsletter(email: string | null) {
  // This would cause an error: console.log(`Sending newsletter to ${email.toUpperCase()}`);
  // Because email might be null and null doesn't have toUpperCase()

  if (email !== null) {
    console.log(`Sending ExplainThis newsletter to ${email.toUpperCase()}`);
  } else {
    console.log("No email provided for newsletter");
  }
}

The | symbol creates what's called a union type, which we'll explore more in future articles. For now, just know that string | null means "this could be a string or it could be null."

The Essential Types Reference

Now that you've seen these types in action, let's create a quick reference for the essential TypeScript types you'll use daily:

// Primitive types
let articleContent: string = "Learn TypeScript basics...";
let readingTime: number = 5;
let isFeatured: boolean = true;

// Array types
let viewCounts: number[] = [100, 250, 180];
let tags: string[] = ["typescript", "javascript"];

// Object types
let reader: { name: string; articlesRead: number } = {
  name: "Alice",
  articlesRead: 15,
};

// Function types
function calculateEngagement(views: number, likes: number): number {
  return views + likes;
}

// Handling missing values
let authorTwitter: string | null = null;
let featuredImage: string | undefined = undefined;

These five categories (primitives, arrays, objects, functions, and nullable types) cover most TypeScript code you'll write. Get comfortable with these, and you'll understand the building blocks of everything else.

When to Add Types Explicitly

You might be wondering when you should add type annotations and when you can rely on inference. A practical rule of thumb:

Let TypeScript infer when:

  • You're assigning a value immediately: let title = "TypeScript Guide"
  • The type is obvious from the context: const viewCounts = [100, 250, 180]
  • You're working with simple expressions: const totalViews = todayViews + yesterdayViews

Be explicit when:

  • Defining function parameters: function publishArticle(title: string)
  • You want to enforce a specific shape: const article: { title: string; wordCount: number } = {}
  • The inferred type is too narrow or too wide for your needs

This balance gives you safety without cluttering your code with unnecessary type annotations. You want TypeScript to catch real bugs, not make you jump through hoops for every variable.

Summary

These basic types (string, number, boolean, arrays, and objects) form the core of TypeScript's type system. Instead of mysterious runtime errors, you get clear warnings right in your editor.

TypeScript doesn't just add syntax to JavaScript. It changes how you work with data. Instead of hoping that calculateReadingTime receives numbers, you know it does. Instead of wondering if that article object has a title property, TypeScript shows you exactly what's there.

You now know the basic building blocks of TypeScript. But what happens when you need a variable that could be a string or a number? What about optional object properties, or arrays that can hold multiple types? We'll cover these more complex scenarios in the next articles.

For now, try converting a simple JavaScript function to use these basic types. You might be surprised how many potential bugs TypeScript catches, even in code you thought was working perfectly.

☕️ Support Us
Your support will help us to continue to provide quality content.👉 Buy Me a Coffee