Transitioning to TypeScript: The Ultimate Starter Guide - Part 4
Tags: typescript, javascript, webdev, beginners
Published: 2023-09-03 11:18:58

## TypeScript from JavaScript Part 4 of 7: Advanced Types and Type Guards

Welcome back to our series on transitioning from JavaScript to TypeScript! If you’ve been following along, by now you should have a good grasp on the basics of TypeScript, including modules, namespaces, and decorators which we covered in the last part. In this part, we are going to dive into advanced types and type guards in TypeScript.

Remember, our goal here is not just to learn TypeScript, but also to understand how it can simplify our work, save time, and ultimately elevate our apps. So let's get started!

## 1. Advanced Types

TypeScript comes with a set of advanced types that help you to model your data more precisely. Let's go through some of the most useful ones.

### a. Union Types

Union types allow you to define a variable that can be of multiple types. You can define a union type using the `|` operator.

```typescript
let value: number | string;

value = 10;
value = 'Hello';
```

In this example, the `value` variable can be either a `number` or a `string`.

### b. Intersection Types

Intersection types allow you to combine multiple types into one. You can define an intersection type using the `&` operator.

```typescript
interface Person {
  name: string;
}

interface Employee {
  id: number;
}

type EmployeeRecord = Person & Employee;

const employee: EmployeeRecord = {
  name: 'Alice',
  id: 1
};
```

In this example, the `EmployeeRecord` type is an intersection of the `Person` and `Employee` types.

### c. Literal Types

Literal types allow you to restrict a variable to a specific value.

```typescript
type Direction = 'north' | 'south' | 'east' | 'west';

const direction: Direction = 'north';
```

In this example, the `direction` variable can only be one of the four specified values.

### d. Mapped Types

Mapped types allow you to create a new type from an existing type by transforming its properties.

```typescript
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = Readonly<Person>;

const person: ReadonlyPerson = {
  name: 'Alice',
  age: 30
};

person.name = 'Bob'; // Error
```

In this example, the `ReadonlyPerson` type is a mapped type that makes all properties of the `Person` type read-only.

### e. Conditional Types

Conditional types allow you to define a type based on a condition.

```typescript
type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false
```

In this example, the `IsString` type is a conditional type that checks if a type is `string`.

## 2. Type Guards

Type guards are expressions that perform a runtime check on the type of a variable.

### a. typeof Type Guards

The `typeof` operator can be used as a type guard to check the type of a variable.

```typescript
function print(value: number | string) {
  if (typeof value === 'number') {
    console.log(value.toFixed(2));
  } else {
    console.log(value.toUpperCase());
  }
}

print(10); // 10.00
print('hello'); // HELLO
```

In this example, the `typeof` operator is used as a type guard to check if the `value` variable is a `number` or a `string`.

### b. instanceof Type Guards

The `instanceof` operator can be used as a type guard to check the instance of a class.

```typescript
class Circle {
  radius: number;

  constructor(radius: number) {
    this.radius = radius;
  }
}

class Square {
  side: number;

  constructor(side: number) {
    this.side = side;
  }
}

function getArea(shape: Circle | Square) {
  if (shape instanceof Circle) {
    return Math.PI * Math.pow(shape.radius, 2);
  } else {
    return Math.pow(shape.side, 2);
  }
}

const circle = new Circle(5);
const square = new Square(5);

console.log(getArea(circle)); // 78.53981633974483
console.log(getArea(square)); // 25
```

In this example, the `instanceof` operator is used as a type guard to check if the `shape` variable is an instance of `Circle` or `Square`.

### c. User-Defined Type Guards

You can also create your own type guards by defining a function that returns a type predicate.

```typescript
interface Person {
  name: string;
}

function isPerson(value: any): value is Person {
  return value && typeof value.name === 'string';
}

function printName(value: any) {
  if (isPerson(value)) {
    console.log(value.name);
  } else {
    console.log('Not a person');
  }
}

const person = { name: 'Alice' };
const notPerson = { age: 30 };

printName(person); // Alice
printName(notPerson); // Not a person
```

In this example, the `isPerson` function is a user-defined type guard that checks if a value is a `Person`.

## Conclusion

In this part, we learned about advanced types and type guards in TypeScript. These features are extremely powerful and allow you to model your data more precisely and perform runtime type checks.

In the next part of this series, we will learn about async/await and error handling in TypeScript. Stay tuned!

That's all for now, happy coding!