DeveloperBreeze

Introduction

TypeScript has become a popular choice for JavaScript developers due to its ability to add static types to dynamic JavaScript code. For experienced developers, understanding TypeScript's advanced type features can greatly enhance the robustness and maintainability of their applications.

This tutorial explores TypeScript's advanced type inference and complex types, helping you write safer and more maintainable code.


Prerequisites

To follow along with this tutorial, you should have:

  • A solid understanding of JavaScript
  • Some experience with TypeScript basics (interfaces, classes, generics)
  • TypeScript installed in your development environment:
npm install -g typescript

Type Inference

TypeScript’s type inference allows the compiler to determine types automatically without explicit annotations.

Variable Type Inference

let name = 'Alice'; // inferred as string
let age = 30;       // inferred as number
let isDeveloper = true; // inferred as boolean
function add(a: number, b: number) {
  return a + b; // inferred return type is number
}

let numbers = [1, 2, 3]; // inferred as number[]

Contextual Typing

const handler = (event: MouseEvent) => {
  console.log(event.button); // inferred as MouseEvent
};

window.onclick = handler;

Advanced Types

Intersection Types

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

interface Employee {
  employeeId: number;
}

type EmployeePerson = Person & Employee;

const john: EmployeePerson = {
  name: 'John Doe',
  age: 35,
  employeeId: 1234
};

Union Types

function printId(id: number | string) {
  console.log('ID:', id);
}

printId(101);
printId('ABC123');

Literal Types

type Direction = 'up' | 'down' | 'left' | 'right';

function move(direction: Direction) {
  console.log('Moving', direction);
}

move('up'); // valid
move('right'); // valid
// move('forward'); // error

Mapped Types

type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

interface Car {
  make: string;
  model: string;
  year: number;
}

const myCar: Readonly<Car> = {
  make: 'Toyota',
  model: 'Corolla',
  year: 2020
};

// myCar.make = 'Honda'; // error

Conditional Types

type IsString<T> = T extends string ? 'string' : 'not string';

type Test1 = IsString<string>; // 'string'
type Test2 = IsString<number>; // 'not string'

Template Literal Types

type Action = 'create' | 'update' | 'delete';
type Entity = 'user' | 'post';

type LogMessage = `${Action}_${Entity}`;

function logAction(action: LogMessage) {
  console.log(`Logging action: ${action}`);
}

logAction('create_user'); // valid
logAction('update_post'); // valid
// logAction('read_user'); // error

Advanced Generics

Generic Constraints

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

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

const name = getProperty(person, 'name'); // valid
const age = getProperty(person, 'age');   // valid
// const gender = getProperty(person, 'gender'); // error

Default Generic Parameters

interface ApiResponse<T = any> {
  data: T;
  status: number;
  error?: string;
}

const response: ApiResponse<{ userId: number }> = {
  data: { userId: 1 },
  status: 200
};

const defaultResponse: ApiResponse = {
  data: {},
  status: 200
};

Utility Types

Partial

interface User {
  name: string;
  email: string;
  age: number;
}

function updateUser(user: User, fieldsToUpdate: Partial<User>) {
  return { ...user, ...fieldsToUpdate };
}

const user: User = { name: 'Alice', email: 'alice@example.com', age: 30 };
const updatedUser = updateUser(user, { email: 'alice@newdomain.com' });

Pick

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, 'title' | 'completed'>;

const todo: TodoPreview = {
  title: 'Buy groceries',
  completed: false
};

Omit

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoSummary = Omit<Todo, 'description'>;

const todo: TodoSummary = {
  title: 'Buy groceries',
  completed: false
};

Record

type PageInfo = {
  title: string;
};

type Page = 'home' | 'about' | 'contact';

const pageInfo: Record<Page, PageInfo> = {
  home: { title: 'Home Page' },
  about: { title: 'About Us' },
  contact: { title: 'Contact Us' }
};

Conclusion

TypeScript's advanced type inference and type features allow you to write more robust, flexible, and maintainable code. In this tutorial, we covered:

  • Intersection types
  • Union types
  • Conditional types
  • Advanced generics
  • Template literal types
  • Utility types (Partial, Pick, Omit, Record)

By leveraging these tools, you can significantly enhance the quality and safety of your TypeScript projects.


Next Steps

  • Explore TypeScript features like decorators and namespaces.
  • Use TypeScript with frameworks like React and Angular for type-safe development.
  • Experiment with custom utility types for complex type transformations.

Continue Reading

Handpicked posts just for you — based on your current read.

Discussion 0

Please sign in to join the discussion.

No comments yet. Start the discussion!