Skip to main content

Advanced Types

TypeScript offers powerful ways to create and manipulate types. Understanding these features helps you write more flexible and maintainable code.

Union and Intersection Types

Combine types using | and &:

// Union type - can be either
type ID = string | number;

// Intersection type - must be both
type Employee = Person & HasRole;

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

interface HasRole {
role: string;
permissions: string[];
}

Mapped Types

Create new types by transforming existing ones:

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

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

// All properties are readonly
type ReadOnlyUser = ReadOnly<User>;

Conditional Types

Types that depend on other types:

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

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

// Practical example
type ArrayType<T> = T extends Array<infer U> ? U : never;
type ItemType = ArrayType<string[]>; // type ItemType = string

Template Literal Types

Create types based on string literals:

type EventName = 'click' | 'focus' | 'blur';
type Handler = `on${Capitalize<EventName>}`;
// type Handler = 'onClick' | 'onFocus' | 'onBlur'

type Path = `/${string}`; // Must start with '/'
type URL = `https://${string}`; // Must start with 'https://'

Index Types

Access and manipulate types using keys:

interface Response {
data: {
user: {
id: number;
name: string;
};
settings: {
theme: string;
};
};
}

// Get nested type
type UserData = Response['data']['user'];
// { id: number; name: string; }

// Get all keys
type ResponseKeys = keyof Response; // 'data'

Type Predicates

Custom type guards with precise control:

interface Cat { meow(): void; }
interface Dog { bark(): void; }

function isCat(animal: Cat | Dog): animal is Cat {
return 'meow' in animal;
}

function handleAnimal(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow(); // TypeScript knows this is safe
} else {
animal.bark(); // Must be a Dog
}
}

Best Practices

  1. Use precise types over general ones
  2. Leverage type inference when possible
  3. Document complex type manipulations
  4. Break down complex types into smaller, reusable parts
  5. Consider performance with large mapped types

Practice Exercise

Create a type system for a form validation library that:

  1. Handles different input types (text, number, email)
  2. Provides type-safe validation rules
  3. Uses mapped types for form field states
  4. Includes conditional error messages based on validation type