Mastering the Core of TypeScript: A Practical Guide to Types

If you’ve spent any time with JavaScript, you’ve likely encountered the infamous undefined is not a function error. It’s a rite of passage. These runtime errors happen because JavaScript is dynamically typed, meaning it only figures out the “type” of a variable (like a string, number, or object) when the code is actually running.

TypeScript changes the game by adding a layer of static types on top of JavaScript. Think of it as a super-smart linter that understands the shape of your data. By defining types *before* you run your code, you can catch a huge class of bugs right in your editor. This is the core promise of TypeScript, and it all starts with understanding its type system.

Let’s dive into the essential TypeScript types you’ll use every day, moving from the simple to the more complex.


The Basics: Type Annotation Syntax

First, how do you even tell TypeScript about a type? You use a colon : after a variable or function parameter name, followed by the type.

let framework: string = "React";
let version: number = 18;
let isAwesome: boolean = true;

// This will now cause an error in your editor!
// version = "eighteen"; // Error: Type 'string' is not assignable to type 'number'.

This simple annotation is your contract with the TypeScript compiler. It lets you declare your intent and helps the compiler enforce it.


The Primitives: Your Daily Drivers

These are the fundamental data types that form the building blocks of any application.

string, number, and boolean

These work exactly as you’d expect. They represent text, numbers (both integers and floats), and true/false values.

let greeting: string = "Hello, world!";
let userCount: number = 1_000_000; // You can use underscores for readability
let hasLoggedIn: boolean = false;

A key thing to note for developers coming from languages like Java or C# is that there’s only one number type in TypeScript for all numeric values.

null and undefined

In TypeScript, null and undefined are considered their own types. They represent the intentional absence of a value and an uninitialized value, respectively.

let selectedUser: string | null = null; // This user might be selected later

function doSomething(callback?: () => void) {
  // The an optional callback will be of type 'undefined' if not provided
  if (callback) {
    callback();
  }
}

Pro Tip: Enable the strictNullChecks option in your tsconfig.json file. It forces you to explicitly handle cases where a value could be null or undefined, which eliminates a vast number of runtime errors.


The Special Types: Handling Absence and Impossibility

TypeScript includes a few special types for specific scenarios, particularly with functions.

void

void is used as the return type for functions that don’t return a value. If you’ve written a function that just logs to the console or updates a variable without returning, its return type is void.

function logMessage(message: string): void {
  console.log(message);
  // No return statement
}

never

This is a more interesting one. never represents a value that *never* occurs. You’ll typically see it in two situations:

  1. A function that always throws an error.
  2. A function that has an infinite loop.

In both cases, the function never reaches a return statement, so it never produces a value.

function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {
    // ...
  }
}

You won’t often write functions that return never, but it’s useful for the type system to understand and model these exhaustive checks.


The Escape Hatches: any and unknown

Sometimes you’re working with data where you truly don’t know the type, like from a dynamic API response or a third-party library without type definitions. TypeScript gives you two “escape hatches” for these situations.

any: The Wild West

The any type is the most flexible type in TypeScript. It essentially tells the compiler, “Trust me, I know what I’m doing,” and opts out of all type checking for that variable.

let myVariable: any = "this is a string";

// No errors, even though this is dangerous!
myVariable.someMethodThatDoesNotExist(); 
myVariable = 42; 

Using any can be a crutch. It’s helpful when migrating a JavaScript codebase to TypeScript, but you should avoid it in new code whenever possible. It defeats the purpose of using TypeScript in the first place.

unknown: The Safer Alternative

unknown is the type-safe counterpart to any. It means, “I don’t know what this is, so you can’t do anything with it until you check.”

let myValue: unknown = "this could be anything";

// myValue.toUpperCase(); // Error: 'myValue' is of type 'unknown'.

// You MUST check the type before using it
if (typeof myValue === 'string') {
  // Now TypeScript knows myValue is a string in this block
  console.log(myValue.toUpperCase());
}

Rule of thumb: Always prefer unknown over any. It forces you to safely inspect a value before operating on it.


Structuring Your Data: Arrays, Tuples, and Objects

Arrays

To declare an array of a certain type, you add square brackets [] after the type.

let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["Alice", "Bob", "Charlie"];

// An alternative syntax using generics
let booleans: Array<boolean> = [true, false, true];

Tuples

A tuple is like an array with a fixed number of elements where the type of each element is known. It’s perfect for when you want to return a fixed set of different types from a function.

// A tuple representing a key-value pair
let entry: [string, number];

entry = ["age", 30]; // Correct

// entry = ["name", "John"]; // Error: Type 'string' is not assignable to type 'number'.
// entry = ["age", 30, true]; // Error: Tuple of length '2' has extra element 'true'.

// A great use case for React hooks
// const [count, setCount] = useState(0); // This is a tuple: [number, (newValue: number) => void]

Objects

For simple, one-off objects, you can define their “shape” inline.

let user: { name: string; id: number; isActive: boolean };

user = {
  name: "Ankur",
  id: 1,
  isActive: true
};

// user.id = "2"; // Error: Type 'string' is not assignable to type 'number'.
// user.email = "[email protected]"; // Error: Property 'email' does not exist on type...

This is powerful because it describes exactly which properties an object should have and what their types should be. For more complex or reusable object shapes, you’ll want to use type aliases or interface, but that’s a topic for another day!


Conclusion

Understanding these fundamental types is the first and most crucial step toward mastering TypeScript. By moving from the Wild West of dynamic JavaScript to the safer, more predictable world of static types, you empower your editor to become an invaluable partner in writing robust, bug-free code.

Start small. The next time you create a variable or write a function, take a moment to add a type annotation. You’ll be amazed at how quickly it becomes second nature and how many potential errors you’ll catch before they ever make it to the browser.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.