Learn TypeScript With Me in 14 Days Day 2

Posted on 2 October 2024 Reading time: 6 min read
Learn TypeScript With Me in 14 Days Day 2

Understanding Basic Types in TypeScript

Today, we will dive into one of the core features of TypeScript: basic types. By the end of this post, we will gain some understanding of how TypeScript’s type system works and how it can help us write more reliable, maintainable code. We’ll explore how to define types for variables and functions, and apply these types to our Task Manager project.

What Are Types in TypeScript?

In JavaScript, variables are dynamic. A variable can hold any value at any time, which is flexible but can lead to bugs that are hard to track down. TypeScript, on the other hand, allows you to declare types for variables, functions, and objects. This way, you can specify what kind of data a variable is allowed to hold, ensuring your code is type-safe.

By adding types, TypeScript helps catch errors at compile time rather than at runtime. This means fewer bugs in production and more confidence in your code.

Let’s try an example of how TypeScript helps to catch these errors by writing the same logic in both JavaScript and TypeScript.

JavaScript Example (No Compile-Time Type Checking)

let value = 42; // Initially a number
console.log(value); // 42

value = "Now it's a string"; // Reassigned to a string
console.log(value); // "Now it's a string"

value.toFixed(2); // Runtime error! .toFixed is a method for numbers, not strings

TypeScript Example (Compile-Time Type Checking)

// TypeScript enforces types, preventing errors early at compile time
let value: number = 42; // Declaring 'value' as a number
console.log(value); // 42

value = "Now it's a string"; // Compilation error: Type 'string' is not assignable to type 'number'

value.toFixed(2); // This is safe since 'value' is always a number

Common Basic Types:

  1. String

    The string type represents textual data.

    let color: string = "blue";
    color = "red";
    
  2. Number

    The number type represents both integers and floating-point values.

    let decimal: number = 6;
    let hex: number = 0xf00d;
    let binary: number = 0b1010;
    let octal: number =  0o744;
    
  3. Boolean

    The boolean type represents true or false values.

    let isDone: boolean = false;
    
  4. Any

    The any type is a way to opt-out of type checking. It can be used when you don’t want a particular value to cause type-check errors.

    let notSure: any = 4;
    notSure = "maybe a string instead";
    notSure = false; // okay, definitely a boolean
    
  5. Array

    A collection of elements of the same type (e.g., number[], string[]).

    let list: number[] = [1, 2, 3];
    let list: Array<number> = [1, 2, 3];
    
  6. Object

    The object type represents any non-primitive type.

    let obj: object = { name: "John", age: 30 };
    

Using The Basic Types

Let’s start by defining some variables with basic types in TypeScript.

  1. Create a New File: Open your project and create a new file named types.ts in the src folder.

     touch src/types.ts
    
  2. Add Some Basic Type Definitions: Add the following code inside types.ts:

    let taskTitle: string = 'Complete TypeScript tutorial';
    let taskId: number = 1;
    let isCompleted: boolean = false;
    
    console.log(`Task: ${taskTitle} (ID: ${taskId}) - Completed? ${isCompleted}`);
    

In this example:

  • taskTitle is typed as a string.
  • taskId is typed as a number.
  • isCompleted is typed as a boolean.

By specifying these types, TypeScript will prevent you from accidentally assigning a value of the wrong type, like trying to assign a string to taskId.

Arrays and Objects

In TypeScript, you can define arrays with a specific type for all the elements it contains. For example, an array of numbers would be typed as number[].

let taskIds: number[] = [1, 2, 3, 4];
taskIds.push(5); // Valid
taskIds.push('six'); // Error: 'six' is not assignable to type 'number'

Here, taskIds is an array that can only hold numbers. If you try to push a string into it, TypeScript will give you a helpful error message.

You can also define types for objects using an interface or inline object types. Let’s define an object type for a task in our Task Manager app:

let task: { id: number, title: string, completed: boolean } = {
  id: 1,
  title: 'Complete TypeScript tutorial',
  completed: false
};

This is an object with three properties, and each property has a defined type. TypeScript ensures that every property follows the declared type.

Defining an Interface for a Task

As your project grows, it is good practice to use interfaces for defining the shape of an object, which makes your code more reusable and easier to maintain. Let’s define an interface for our Task object:

interface Task {
  id: number;
  title: string;
  completed: boolean;
}

let task1: Task = {
  id: 1,
  title: 'Complete TypeScript tutorial',
  completed: false
};

By using this interface, you can now use the Task type throughout your code. If you accidentally forget a property or use an incorrect type, TypeScript will warn you.

Functions and Types

You can also define types for function parameters and return values. This ensures that your functions receive the correct input and produce the correct output.

function toggleTaskCompletion(task: Task): Task {
  return { ...task, completed: !task.completed };
}

const updatedTask = toggleTaskCompletion(task1);
console.log(updatedTask);

In this example:

  • The toggleTaskCompletion function takes a Task as a parameter and returns a Task object with the completed field toggled.
  • { …task } This is using the “spread syntax” (…) to create a shallow copy of the task object. It takes all the properties of the task and copies them into a new object.
  • completed: !task.completed: This updates the completed property of the copied task. The ! operator negates the value of task.completed. If it was true, it becomes false, and vice versa.

TypeScript checks that the parameter passed to the function is a valid Task object and that the function’s return type matches the expected Task type.

Applying Types to the Task Manager Project

Now that we understand the basics of TypeScript types, let’s apply them to our Task Manager project.

1. Update the Task Interface:

In your project, create a file named task.ts inside the src folder. This file will contain the Task interface and any related types.

touch src/task.ts

Add the Task interface to the task.ts file:

export interface Task {
  id: number;
  title: string;
  completed: boolean;
}

Now you can import this Task interface wherever you need it in your project.

2. Using the Task Interface in Code:

Let’s modify index.ts to use the Task interface we just defined. First, import the Task interface:

import { Task } from './task';

let tasks: Task[] = [
  { id: 1, title: 'Learn TypeScript', completed: false },
  { id: 2, title: 'Build a Task Manager app', completed: false }
];

console.log(tasks);

Here, we’ve created an array of Task objects. TypeScript will now enforce that every object in the tasks array conforms to the Task interface.

What’s Next?

Now that we’ve learnt about basic types and applied them to our Task Manager project, we’ll build on this foundation by exploring arrays, objects, and functions in more depth in the next session.

I hope you found today’s session informative and practical! Tomorrow, we’ll continue our journey with Day 3: Arrays and Objects in TypeScript.

Useful Resources:

TypeScript Documentation

TypeScript Playground(Great for testing code snippets)

If you have any questions or run into any issues, you can contact me at steve [at] stephenpopoola.uk.