A Beginner's Guide to TypeScript Types and Interfaces

A Beginner's Guide to TypeScript Types and Interfaces

·

11 min read

JavaScript has long been a leading language in the dynamic world of software development. However, as applications get more complex, it becomes clear that stronger tools are required to handle this complexity and guarantee code stability. In this situation, TypeScript is useful.

The benefits of static typing are brought to JavaScript through TypeScript, a statically typed superset of JavaScript that compiles to plain JavaScript. This includes increased tooling features including autocompletion, type checking, and inferred types, as well as error detection that occurs at compile time rather than runtime.

This article is meant to introduce new TypeScript users to the language. We'll start by setting up your working environment before exploring some of TypeScript's fundamental types. We will also examine how type annotations are used and how TypeScript determines types. Finally, we'll look into TypeScript interfaces, a useful tool for specifying contracts in your code and how to extend them for more complex structures.

This article intends to provide you with a general understanding of TypeScript, whether you're a seasoned JavaScript developer thinking about switching to TypeScript or a rookie just starting your software development adventure. Let's get started with this informative investigation.

Setting up the Environment for TypeScript

Before you can start writing TypeScript, you need to set up your development environment. This involves installing Node.js and TypeScript. Here’s a step-by-step guide on how to do this:

Step 1: Install Node.js

Node.js is an open-source, cross-platform JavaScript runtime environment that executes JavaScript code outside a web browser. It comes with npm (node package manager) which we will use to install TypeScript.

  1. Download Node.js from the official website: https://nodejs.org

  2. Choose the version that is appropriate for your operating system (Windows, MacOS, Linux).

  3. Run the installer and follow the prompts to install Node.js and npm.

You can verify the installation by opening a terminal or command prompt and typing:

$ node -v
$ npm -v

This should display the installed versions of Node.js and npm respectively.

Step 2: Install TypeScript

Once you have Node.js installed, you can install TypeScript globally on your computer by running the following command in your terminal or command prompt:

$ npm install -g typescript

You can check the successful installation of TypeScript by running:

$ tsc -v

This should display the installed version of TypeScript.

Step 3: Set Up Your Code Editor

You can write TypeScript code in any text editor (like Vim, Sublime Text, etc.), but IDEs/code editors like Visual Studio Code are recommended because they have great support for TypeScript out of the box, including features like autocompletion, syntax highlighting, and an integrated terminal.

With these steps, your development environment is ready, and you can start writing JavaScript applications using TypeScript.

Basic Types TS provides

TypeScript includes several basic (and most used) types to help you write robust and maintainable code. Here are some of them:

  1. Boolean: This is a simple true/false value. For example, let isDone: boolean = false;

  2. Number: This is a numeric data type. TypeScript supports decimal, hexadecimal, binary and octal literals. For example, let decimal: number = 6;

  3. String: This is a textual data type. For example, let color: string = "blue";

  4. Array: TypeScript allows you to work with arrays of types. For example, let list: number[] = [1, 2, 3]; or let list: Array<number> = [1, 2, 3];

  5. Tuple: Tuple types allow you to express an array with a fixed number of elements whose types are known but need not be the same. For example, let x: [string, number] = ["hello", 10];

  6. Enum: Enums allow us to define a set of named constants. For example, enum Color {Red, Green, Blue}; let c: Color = Color.Green;

  7. Any: We may need to describe the type of variables that we do not know when we are writing an application. These values may come from dynamic content such as from the user or a third-party library. In these cases, we want to opt-out of type checking and let the values pass through compile-time checks. We can use the any type for this. For example, let notSure: any = 4;

  8. Void: void is a little like the opposite of any: the absence of having any type at all. You may commonly see this as the return type of functions that do not return a value. For example, function warnUser(): void { console.log("This is my warning message"); }

  9. Null and Undefined: In TypeScript, both undefined and null have their types named undefined and null respectively.

  10. Never: The never type represents the type of values that never occur. For instance, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns.

These are just some of the basic types in TypeScript that help in writing more robust code by providing compile-time checks and reducing runtime errors.

Type Annotations in TypeScript

Type annotations in TypeScript are lightweight ways to record the intended contract of the function or variable. In other words, they are a way of providing extra information about your code to the TypeScript compiler to ensure you use your code correctly.

Importance of Type Annotations

Type annotations are important for a few reasons:

  1. Type Safety: They ensure type safety by making the TypeScript compiler aware of the data type, which helps catch errors at compile time rather than at runtime.

  2. Self-documenting Code: They make the code self-explanatory, improving readability and maintainability.

  3. Intellisense: They provide better autocompletion, help, and parameter info in code editors that support TypeScript.

Examples of Type Annotations

Here are some examples of how to use type annotations in TypeScript:

  1. Variable Type Annotation: You can specify the type of a variable like this:
let name: string = 'John Doe';

In this example, name is a string. If you try to assign a number or boolean value to name, the TypeScript compiler will throw an error.

  1. Function Parameter Type Annotation: You can specify the types of function parameters like this:
function greet(name: string, age: number): void {
    console.log(`Hello, my name is ${name} and I am ${age} years old.`);
}

In this example, the greet function expects two parameters: a string and a number. If you try to call this function with arguments of different types, the TypeScript compiler will throw an error.

  1. Function Return Type Annotation: You can specify the return type of a function like this:
function add(a: number, b: number): number {
    return a + b;
}

In this example, the add function is expected to return a number. If you try to return a value of a different type, the TypeScript compiler will throw an error.

By using type annotations, you can make your TypeScript code more robust and easier to understand.

Type Inference in TypeScript

Type inference is a powerful feature in TypeScript, which allows the compiler to automatically infer the type of a variable when there is no explicit type annotation.

Understanding Type Inference

In TypeScript, you don’t always have to explicitly annotate types. If you don’t specify a type, TypeScript will use type inference to provide a type. This means that TypeScript will try to figure out the type of a variable on its own.

Here’s an example:

let message = "Hello World";

In this case, TypeScript can infer that message is of type string because it’s initialized with a string. You don’t need to write let message: string = "Hello World"; - TypeScript understands this implicitly.

Benefits of Type Inference

Type inference can make your code cleaner and less verbose. It’s also useful when dealing with complex types or working with libraries where the types might be difficult to annotate manually.

However, it’s important to note that while type inference can make code more concise, explicit type annotations can make your code more understandable for other developers by clearly stating the expected type of a variable or function return value.

When Does TypeScript Infer Types?

TypeScript uses type inference in a few different scenarios:

  1. Variable and Member Initialization: As shown in the example above, TypeScript infers the type based on the initial value of the variable.

  2. Parameter Default Values: If a parameter has a default value, TypeScript infers the type of the parameter from it.

  3. Function Return Types: If a function doesn’t have a return type annotation, TypeScript infers its return type based on the return statements in the function.

In conclusion, understanding how and when TypeScript infers types can help you write more concise and effective TypeScript code.

Interfaces in TypeScript

Interfaces are a powerful way to define contracts within your code in TypeScript. They are primarily used to define the shape of an object, ensuring that the object has certain properties with specific types.

Understanding Interfaces

An interface in TypeScript is a syntactical contract that an entity should conform to. In other words, an interface defines the syntax that any entity must adhere to.

Interfaces are not to be confused with classes. They are not templates for object creation like classes, but rather, they are a way of defining the “shape” or structure that objects should take.

Here’s a simple example of an interface:

interface Person {
    firstName: string;
    lastName: string;
}

In this example, Person is an interface that expects an object to have firstName and lastName properties, both of type string.

Using Interfaces

You can use an interface to type-check whether an object fits a certain structure. For instance:

function greet(person: Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "John", lastName: "Doe" };

console.log(greet(user)); // Outputs: Hello, John Doe

In this example, the greet function expects a Person object. If you try to pass an object that doesn’t conform to the Person interface, TypeScript will throw a compile-time error.

In conclusion, interfaces in TypeScript provide a way to define contracts for complex structures and ensure that your objects adhere to these contracts.

Defining Interfaces in TypeScript

In TypeScript, an interface can include properties and methods that an object should have. Here’s how you can define an interface:

Interface with Properties

interface Rectangle {
    width: number;
    height: number;
}

In this example, the Rectangle interface expects an object to have width and height properties, both of type number.

Interface with Methods

Interfaces can also define methods that an object should have:

interface Greeter {
    greet(name: string): string;
}

In this example, the Greeter interface expects an object to have a greet method that takes a string parameter and returns a string.

Optional Properties

Sometimes, not all properties of an interface may be required. Such optional properties are denoted by a ? after the property name:

interface Employee {
    name: string;
    id?: number; // id is optional
}

In this example, the Employee interface expects an object to have a name property of type string, and optionally an id property of type number.

Readonly Properties

TypeScript provides a way to mark a property as read-only. This means that once a property is assigned a value, it cannot be changed:

interface Point {
    readonly x: number;
    readonly y: number;
}

In this example, the Point interface expects an object to have x and y properties of type number. Once assigned, these properties cannot be changed.

In conclusion, interfaces in TypeScript are a powerful way to define the shape of objects. They provide a way to ensure objects have certain properties and methods, and they support optional and readonly properties for added flexibility.

Extending Interfaces

In TypeScript, interfaces can extend each other just like classes. This allows you to create more complex structures from simple ones, promoting code reusability and maintainability.

Understanding Interface Extension

When one interface extends another, it inherits all the properties and methods of the base interface. You can then add more properties or methods to the derived interface.

Here’s an example:

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

In this example, Square extends Shape, so it has both color and sideLength properties.

Using Extended Interfaces

You can use extended interfaces just like any other interface:

let square: Square = {color: "blue", sideLength: 10};

In this example, square is of type Square, so it needs to have both color and sideLength properties.

Extending Multiple Interfaces

A TypeScript interface can extend multiple interfaces, creating a combination of all the interfaces:

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

In this example, Square extends both Shape and PenStroke, so it has color, penWidth, and sideLength properties.

In conclusion, extending interfaces is a powerful feature in TypeScript that allows you to create complex structures from simpler ones, promoting code reusability and maintainability.

Conclusion

The robustness, clarity, and maintainability of your code are significantly enhanced by the powerful features of TypeScript’s types and interfaces. These features are invaluable tools, whether you’re an experienced JavaScript developer or a novice in web development. Gaining proficiency in these concepts will unquestionably elevate your TypeScript coding prowess. (Not that it's enough to make a good piece of software, check out this article to know why)

Thanks for reading, like and follow for more articles like this!