Flash cards
Review the key moves
What is the main idea behind TypeScript Advanced Types?
Lesson checks
Practice each idea before moving on
Short Mimo-style checks built from this lesson's code, terms, and sequence.
Which statement best captures the main point of this lesson?
Complete the missing token from the example code.
// ___ all properties to boolean type Flags<T> = { [K in keyof T]: boolean;Put the learning moves in the order that makes the concept easiest to apply.
Advanced TypeScript Types
TypeScript's advanced type system allows you to model complex type relationships with precision.
These features are particularly useful for building robust, maintainable applications with excellent type safety.
Key Advanced Type Features
- Mapped Types : Transform properties of existing types
- Conditional Types : Create types based on conditions
- Template Literal Types : Build types using string templates
- Utility Types : Built-in type helpers for common transformations
- Recursive Types : Self-referential types for tree-like structures
- Type Guards & Type Predicates : Runtime type checking
- Type Inference : Advanced pattern matching with infer
Mapped Types
Mapped types allow you to create new types by transforming properties of existing types.
Basic Mapped Type
Transform every property of an object type into a new type using a single template.
Example
// Convert all properties to boolean type Flags<T> = { [K in keyof T]: boolean;
};
interface User {
id: number;
name: string;
email: string;
}
type UserFlags = Flags<User>;
// Equivalent to: // { // id: boolean; // name: boolean; // email: boolean; // }Mapped Type Modifiers
Add or remove property modifiers like readonly and ? across all keys.
Example
// Make all properties optional interface Todo { title: string; description: string; completed: boolean;
}
type OptionalTodo = {
[K in keyof Todo]?: Todo[K];
};
// Remove 'readonly' and '?' modifiers type Concrete<T> = { -readonly [K in keyof T]-?: T[K];
};
// Add 'readonly' and 'required' to all properties type ReadonlyRequired<T> = { +readonly [K in keyof T]-?: T[K];
};Key Remapping
Rename or filter keys while mapping using as , string helpers, and conditional checks.
Example
// Add prefix to all property names type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
// { // getId: () => number; // getName: () => string; // getEmail: () => string; // } // Filter out properties type MethodsOnly<T> = { [K in keyof T as T[K] extends Function ? K : never]: T[K];
};Conditional Types
Conditional types allow you to define types that depend on a condition.
Basic Conditional Types
Select between types based on a condition checked at the type level.
Example
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
type C = IsString<'hello'>; // true
type D = IsString<string | number>; // boolean
// Extract array element type type ArrayElement<T> = T extends (infer U)[] ? U : never; type Numbers = ArrayElement<number[]>; // numberInfer Keyword
Capture a part of a type within a conditional type by introducing a new type variable with infer .
Example
// Get return type of a function type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; // Get parameter types as a tuple type Parameters<T> = T extends (...args: infer P) => any ? P : never; // Get constructor parameter types type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never; // Get instance type from a constructor type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;Distributed Conditional Types
Understand how conditionals distribute over unions versus when they are wrapped to prevent distribution.
Example
// Without distribution type ToArrayNonDist<T> = T extends any ? T[] : never; type StrOrNumArr = ToArrayNonDist<string | number>; // (string | number)[] // With distribution type ToArray<T> = [T] extends [any] ? T[] : never; type StrOrNumArr2 = ToArray<string | number>; // string[] | number[] // Filter out non-string types type FilterStrings<T> = T extends string ? T : never; type Letters = FilterStrings<'a' | 'b' | 1 | 2 | 'c'>; // 'a' | 'b' | 'c'Template Literal Types
Template literal types allow you to build types using template literal syntax.
Basic Template Literal Types
Constrain strings to specific patterns using template literals and unions.
Example
type Greeting = `Hello, ${string}`;
const validGreeting: Greeting = 'Hello, World!';
const invalidGreeting: Greeting = 'Hi there!'; // Error
// With unions type Color = 'red' | 'green' | 'blue'; type Size = 'small' | 'medium' | 'large'; type Style = `${Color}-${Size}`; // 'red-small' | 'red-medium' | 'red-large' | // 'green-small' | 'green-medium' | 'green-large' | // 'blue-small' | 'blue-medium' | 'blue-large'String Manipulation Types
Apply built-in helpers to transform string literal types (uppercasing, capitalizing, etc.).
Example
// Built-in string manipulation types type T1 = Uppercase<'hello'>; // 'HELLO' type T2 = Lowercase<'WORLD'>; // 'world' type T3 = Capitalize<'typescript'>; // 'Typescript' type T4 = Uncapitalize<'TypeScript'>; // 'typeScript' // Create an event handler type type EventType = 'click' | 'change' | 'keydown'; type EventHandler = `on${Capitalize<EventType>}`; // 'onClick' | 'onChange' | 'onKeydown'Advanced Patterns
Compose templates with inference and key remapping to extract metadata and generate APIs.
Example
// Extract route parameters type ExtractRouteParams<T> = T extends `${string}:${infer Param}/${infer Rest}` ? { [K in Param | keyof ExtractRouteParams<`${Rest}`>]: string } : T extends `${string}:${infer Param}` ? { [K in Param]: string } : {}; type Params = ExtractRouteParams<'/users/:userId/posts/:postId'>; // { userId: string; postId: string; } // Create a type-safe event emitter type EventMap = { click: { x: number; y: number }; change: string; keydown: { key: string; code: number };
};
type EventHandlers = {
[K in keyof EventMap as `on${Capitalize<K>}`]: (event: EventMap[K]) => void;
};Utility Types
TypeScript provides several built-in utility types for common type transformations.
Common Utility Types
Use built-ins like Partial , Pick , and Omit for common transformations.
Example
// Basic types interface User { id: number; name: string; email: string; createdAt: Date;
}
// Make all properties optional type PartialUser = Partial<User>; // make all properties required type RequiredUser = Required<PartialUser>; // make all properties read-only type ReadonlyUser = Readonly<User>; // pick specific properties type UserPreview = Pick<User, 'id' | 'name'>; // omit specific properties type UserWithoutEmail = Omit<User, 'email'>; // extract property types type UserId = User['id']; // number type UserKeys = keyof User; // 'id' | 'name' | 'email' | 'createdAt'Advanced Utility Types
Exclude or extract members from unions and create custom mapped helpers.
Example
// Create a type that excludes null and undefined type NonNullable<T> = T extends null | undefined ? never : T; // Exclude types from a union type Numbers = 1 | 2 | 3 | 'a' | 'b'; type JustNumbers = Exclude<Numbers, string>; // 1 | 2 | 3 // Extract types from a union type JustStrings = Extract<Numbers, string>; // 'a' | 'b' // Get the type that is not in the second type type A = { a: string; b: number; c: boolean }; type B = { a: string; b: number }; type C = Omit<A, keyof B>; // { c: boolean } // Create a type with all properties as mutable type Mutable<T> = { -readonly [K in keyof T]: T[K];
};Recursive Types
Recursive types are useful for modeling tree-like data structures where a type can reference itself.
Basic Recursive Type
Model self-referential structures like trees and nested JSON.
Example
// Simple binary tree type BinaryTree<T> = { value: T; left?: BinaryTree<T>; right?: BinaryTree<T>;
};
// JSON-like data structure type JSONValue = | string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }; // Nested comments type Comment = { id: number; content: string; replies: Comment[]; createdAt: Date;
};Advanced Recursive Types
Express linked lists, directory trees, and recursive state machines.
Example
// Type for a linked list type LinkedList<T> = { value: T; next: LinkedList<T> | null;
};
// Type for a directory structure type File = { type: 'file'; name: string; size: number;
};
type Directory = {
type: 'directory';
name: string;
children: (File | Directory)[];
};
// Type for a state machine type State = { value: string; transitions: { [event: string]: State;
};
};
// Type for a recursive function type RecursiveFunction<T> = (x: T | RecursiveFunction<T>) => void;When to Use Advanced Types
- Use mapped types when you need to transform multiple properties of an object type
- Use conditional types when your type depends on another type
- Use template literal types for string manipulation and pattern matching
- Use utility types for common transformations (prefer built-in ones when possible)
- Use recursive types for tree-like or nested data structures
Performance Considerations
- Deeply nested recursive types can slow down the TypeScript compiler
- Very large union types (100+ members) can cause performance issues
- Use type aliases to break down complex types
Type Inference Issues
- Conditional types distribute over union types, which can be surprising
- Type inference with infer works differently in different contexts
- Some utility types don't work well with any or unknown
Maintainability
- Overusing complex types can make code hard to understand
- Document complex type transformations with comments
- Consider using type assertions or helper functions for very complex types