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
- Use precise types over general ones
- Leverage type inference when possible
- Document complex type manipulations
- Break down complex types into smaller, reusable parts
- Consider performance with large mapped types
Practice Exercise
Create a type system for a form validation library that:
- Handles different input types (text, number, email)
- Provides type-safe validation rules
- Uses mapped types for form field states
- Includes conditional error messages based on validation type