Advanced TypeScript: Conditional Types and Type Manipulation
Master TypeScript's most advanced features: recursive conditional types, type-level programming, and building complex type systems for React applications.
Topics Covered:
Prerequisites:
- TypeScript Generics and Utility Types Deep Dive
- Creating Your Own React Library
Overview
This expert-level tutorial explores TypeScript's most advanced type system features. You'll learn recursive conditional types, type-level programming techniques, advanced type inference patterns, and how to build complex type systems that provide incredible type safety for React applications. These techniques enable you to create type-safe abstractions that would be impossible in other languages, catching entire classes of errors at compile time.
Lesson 1: Recursive Conditional Types
Recursive conditional types enable type-level computation and complex type transformations. Recursive Patterns: • Types that reference themselves • Conditional recursion • Type-level loops • Deep type transformations Use Cases: • Deep type utilities • Complex type extraction • Type-level algorithms • Schema validation types
// Deep type operations with recursion
// Deep readonly (recursive)
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends any[]
? ReadonlyArray<DeepReadonly<T[P][number]>>
: DeepReadonly<T[P]>
: T[P];
};
// Deep required
type DeepRequired<T> = {
[P in keyof T]-?: T[P] extends object
? DeepRequired<T[P]>
: T[P];
};
// Extract all string paths from object
type Paths<T> = T extends object
? {
[K in keyof T]: K extends string
? T[K] extends object
? K | `${K}.${Paths<T[K]>}`
: K
: never;
}[keyof T]
: never;
interface User {
profile: {
name: string;
address: {
street: string;
city: string;
};
};
settings: {
theme: string;
};
}
type UserPaths = Paths<User>;
// "profile" | "profile.name" | "profile.address" | "profile.address.street" | ...
// Get value type at path
type PathValue<T, P extends Paths<T>> = P extends `${infer K}.${infer Rest}`
? K extends keyof T
? PathValue<T[K], Rest>
: never
: P extends keyof T
? T[P]
: never;
type StreetType = PathValue<User, "profile.address.street">; // stringRecursive conditional types enable deep type transformations and type-level computation. Use conditional types with recursion to traverse nested structures and build powerful type utilities.
Lesson 2: Type-Level Programming
Type-level programming performs computations at the type level, catching logic errors at compile time. Concepts: • Type-level functions • Type-level conditionals • Type-level loops • Type-level arithmetic • Type-level data structures Applications: • Schema validation • API type generation • Type-safe routing • Complex business logic validation
// Type-level arithmetic (simplified)
type Length<T extends readonly any[]> = T["length"];
type A = Length<[1, 2, 3]>; // 3
// Type-level array operations
type Head<T extends readonly any[]> = T extends readonly [infer H, ...any[]]
? H
: never;
type Tail<T extends readonly any[]> = T extends readonly [any, ...infer Rest]
? Rest
: never;
type Last<T extends readonly any[]> = T extends readonly [...any[], infer L]
? L
: never;
// Type-level string manipulation
type Split<
S extends string,
D extends string
> = S extends `${infer First}${D}${infer Rest}`
? [First, ...Split<Rest, D>]
: [S];
type Parts = Split<"a.b.c", ".">; // ["a", "b", "c"]
// Type-safe state machine
type State = "idle" | "loading" | "success" | "error";
type ValidTransition<S extends State> = S extends "idle"
? "loading"
: S extends "loading"
? "success" | "error"
: never;
function transition<S extends State>(
current: S,
next: ValidTransition<S>
): ValidTransition<S> {
return next;
}
// Type-safe, compile-time validated state transitions
transition("idle", "loading"); // ✅
transition("loading", "success"); // ✅
transition("idle", "success"); // ❌ ErrorType-level programming performs computations in the type system. Build type-level functions, conditionals, and data structures to validate logic at compile time. This catches entire classes of errors before runtime.
Lesson 3: Advanced Type Inference
Master TypeScript's type inference system to create APIs that infer types automatically. Inference Techniques: • Inference from function parameters • Inference from return types • Conditional inference • Variadic tuple types • Const assertions Benefits: • Better developer experience • Automatic type detection • Less manual typing • More flexible APIs
// Inference from usage
function createStore<T>(initialValue: T) {
let value = initialValue;
return {
get: () => value,
set: (newValue: T) => {
value = newValue;
},
};
}
const store = createStore(0); // T inferred as number
const count = store.get(); // number
// Variadic tuple types
function zip<A extends readonly any[], B extends readonly any[]>(
a: A,
b: B
): {
[K in keyof A]: [A[K], B[K extends keyof B ? K : never]];
} {
return a.map((item, i) => [item, b[i]]) as any;
}
const zipped = zip([1, 2, 3] as const, ["a", "b", "c"] as const);
// [[1, "a"], [2, "b"], [3, "c"]]
// Const assertions
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3,
} as const;
// All properties are readonly literals
// Function overloads with inference
function createElement<T extends keyof HTMLElementTagNameMap>(
tag: T
): HTMLElementTagNameMap[T];
function createElement<T extends React.ComponentType<any>>(
component: T
): React.ComponentProps<T>;
function createElement(tag: any): any {
// Implementation
}
const div = createElement("div"); // HTMLDivElement
const button = createElement("button"); // HTMLButtonElement
// Infer component props
type InferProps<T> = T extends React.ComponentType<infer P> ? P : never;
// Type-safe event emitter
type EventMap = {
click: { x: number; y: number };
change: { value: string };
submit: { data: FormData };
};
class TypedEventEmitter<T extends Record<string, any>> {
private listeners: {
[K in keyof T]?: Array<(event: T[K]) => void>;
} = {};
on<K extends keyof T>(event: K, handler: (data: T[K]) => void) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(handler);
}
emit<K extends keyof T>(event: K, data: T[K]) {
this.listeners[event]?.forEach((handler) => handler(data));
}
}
const emitter = new TypedEventEmitter<EventMap>();
emitter.on("click", (data) => {
// data is { x: number; y: number }
});
emitter.emit("click", { x: 10, y: 20 }); // Type-safe!Advanced type inference creates APIs that automatically detect and enforce types. Use variadic tuple types, const assertions, and conditional inference to build flexible, type-safe APIs that require minimal manual typing.
Lesson 4: Building Complex Type Systems
Combine all advanced TypeScript features to build complex, type-safe systems. System Building: • Type-safe API clients • Schema validation types • Form state types • Routing types • State management types Principles: • Start with simple types • Compose complex types • Leverage inference • Document complex types • Test with examples
// Type-safe API client builder
type EndpointDefinition = {
[K: string]: (params?: any) => Promise<any>;
};
type ApiClient<T extends EndpointDefinition> = {
[K in keyof T]: T[K] extends (params: infer P) => Promise<infer R>
? P extends undefined
? () => Promise<R>
: (params: P) => Promise<R>
: never;
};
function createApiClient<T extends EndpointDefinition>(
config: T
): ApiClient<T> {
// Implementation
return {} as ApiClient<T>;
}
const api = createApiClient({
getUser: (id: number) => Promise.resolve({ id, name: "Alice" }),
createPost: (data: { title: string; content: string }) =>
Promise.resolve({ id: 1, ...data }),
});
// Fully type-safe!
const user = await api.getUser(123);
const post = await api.createPost({ title: "Hello", content: "World" });
// Type-safe form state
type FormField<T> = {
value: T;
error?: string;
touched: boolean;
};
type FormState<T extends Record<string, any>> = {
[K in keyof T]: FormField<T[K]>;
} & {
isValid: boolean;
isSubmitting: boolean;
};
function createFormState<T extends Record<string, any>>(
initialValues: T
): FormState<T> {
// Implementation
return {} as FormState<T>;
}
const form = createFormState({
email: "",
password: "",
age: 0,
});
// form.email.value is string
// form.age.value is number
// form.isValid is booleanComplex type systems combine all advanced TypeScript features. Build type-safe APIs, form systems, routing, and state management with full compile-time validation. These systems catch errors before runtime and provide excellent developer experience.
Conclusion
Advanced TypeScript features enable incredible type safety. Recursive conditional types perform type-level computation, type-level programming validates logic at compile time, and advanced inference creates flexible APIs. Build complex type systems that catch entire classes of errors before runtime. Remember: these are powerful tools that require careful design. Start simple, add complexity gradually, and always prioritize readability and maintainability over cleverness.