TypeScript’s type system is Turing-complete — you can encode complex invariants that catch bugs at compile time instead of runtime. Mapped types transform object shapes, conditional types branch on type predicates, template literal types compose string patterns, and infer extracts types from other types. Claude Code generates the generic utility types, type-safe patterns, and complex type-level logic that makes TypeScript’s full power accessible.
CLAUDE.md for TypeScript Type-Heavy Projects
## TypeScript Configuration
- strict: true — no exceptions
- noUncheckedIndexedAccess: true — array access returns T | undefined
- exactOptionalPropertyTypes: true — undefined !== absent
- Use branded types for IDs (type OrderId = string & { _brand: 'OrderId' })
- Avoid type assertions (as Type) — use type guards instead
- Prefer unknown over any — narrow with type guards before use
- Use satisfies operator instead of type annotation when you want inference + checking
Conditional Types
// Type that unwraps promises recursively
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
// Built-in since TS 4.5 — shown for illustration
// Extract the element type of an array
type ElementOf<T extends any[]> = T[number];
// ElementOf<string[]> = string
// ElementOf<[1, "two", true]> = 1 | "two" | true
// IsNever utility
type IsNever<T> = [T] extends [never] ? true : false;
// Deep required: remove all optional modifiers recursively
type DeepRequired<T> =
T extends object
? { [K in keyof T]-?: DeepRequired<T[K]> }
: T;
// Deep partial:
type DeepPartial<T> =
T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;
// NonNullableDeep: strip null/undefined recursively
type NonNullableDeep<T> =
T extends null | undefined ? never :
T extends object ? { [K in keyof T]: NonNullableDeep<T[K]> } :
T;
// Extract object keys where the value extends a given type
type KeysOfType<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
interface Order {
id: string;
totalCents: number;
isPaid: boolean;
items: OrderItem[];
notes: string | null;
}
type StringKeys = KeysOfType<Order, string>; // "id"
type NumberKeys = KeysOfType<Order, number>; // "totalCents"
type BooleanKeys = KeysOfType<Order, boolean>; // "isPaid"
Mapped Types for Transformation
// Make specific keys required, rest optional
type WithRequired<T, K extends keyof T> =
Omit<T, K> & Required<Pick<T, K>>;
type OrderPreview = WithRequired<Order, 'id' | 'totalCents'>;
// { id: string; totalCents: number; isPaid?: boolean; items?: OrderItem[]; notes?: string | null }
// Make specific keys readonly
type WithReadonly<T, K extends keyof T = keyof T> =
Omit<T, K> & Readonly<Pick<T, K>>;
// Rename keys: PrefixedKeys<Order, 'order_'> → { order_id, order_totalCents, ... }
type PrefixedKeys<T, P extends string> = {
[K in keyof T as K extends string ? `${P}${K}` : never]: T[K];
};
// Getter/setter pair type
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
type OrderAccessors = Getters<Pick<Order, 'id' | 'totalCents'>> & Setters<Pick<Order, 'totalCents'>>;
// { getId(): string; getTotalCents(): number; setTotalCents(value: number): void }
Template Literal Types
// Route builder: type-safe URL templates
type ExtractParams<T extends string> =
T extends `${infer _Start}:${infer Param}/${infer Rest}`
? Param | ExtractParams<`/${Rest}`>
: T extends `${infer _Start}:${infer Param}`
? Param
: never;
type RouteParams<Route extends string> = {
[K in ExtractParams<Route>]: string;
};
function buildRoute<Route extends string>(
route: Route,
params: RouteParams<Route>,
): string {
return route.replace(/:([a-zA-Z]+)/g, (_, key) => params[key as keyof typeof params]);
}
const url = buildRoute('/orders/:orderId/items/:itemId', {
orderId: 'ord_123', // ✅
itemId: 'item_456', // ✅
// extra: 'x' // ✅ TypeScript error: extra is not in route params
});
// Event names from object keys
type EventMap<T extends object> = {
[K in keyof T as `on${Capitalize<string & K>}Changed`]: (
prev: T[K], next: T[K]
) => void;
};
type OrderEvents = EventMap<Pick<Order, 'status' | 'totalCents'>>;
// { onStatusChanged: (prev, next) => void; onTotalCentsChanged: (prev, next) => void }
infer — Extracting Types
// Extract return type of async function (unwrapping Promise)
type AsyncReturnType<T extends (...args: any[]) => Promise<any>> =
T extends (...args: any[]) => Promise<infer R> ? R : never;
async function fetchOrder(id: string): Promise<Order> { /* ... */ }
type OrderResult = AsyncReturnType<typeof fetchOrder>; // Order
// Extract parameters of a function
type FirstParam<T extends (...args: any[]) => any> =
T extends (first: infer F, ...rest: any[]) => any ? F : never;
// Extract value type from Map
type MapValue<T extends Map<any, any>> =
T extends Map<any, infer V> ? V : never;
// Flatten array one level
type Flatten<T> = T extends Array<infer Item> ? Item : T;
// Extract resolve type from Promise, leave non-Promises unchanged
type MaybeAwaited<T> = T extends Promise<infer U> ? U : T;
Type-Safe Event Emitter
// A fully type-safe event emitter
type EventHandler<T> = T extends void ? () => void : (data: T) => void;
class TypedEventEmitter<Events extends Record<string, unknown>> {
private handlers: Partial<{ [K in keyof Events]: Set<EventHandler<Events[K]>> }> = {};
on<K extends keyof Events>(event: K, handler: EventHandler<Events[K]>): this {
if (!this.handlers[event]) {
this.handlers[event] = new Set();
}
(this.handlers[event] as Set<EventHandler<Events[K]>>).add(handler);
return this;
}
off<K extends keyof Events>(event: K, handler: EventHandler<Events[K]>): this {
this.handlers[event]?.delete(handler as EventHandler<Events[K]>);
return this;
}
emit<K extends keyof Events>(
...args: Events[K] extends void ? [event: K] : [event: K, data: Events[K]]
): void {
const [event, data] = args;
this.handlers[event]?.forEach(h => (h as Function)(data));
}
}
// Usage:
interface OrderEvents {
created: Order;
updated: { id: string; changes: Partial<Order> };
deleted: { id: string };
synced: void;
}
const emitter = new TypedEventEmitter<OrderEvents>();
emitter.on('created', (order) => { // order: Order ✅
console.log(order.id);
});
emitter.on('updated', ({ id, changes }) => { // Destructured correctly ✅
console.log(id, changes);
});
emitter.emit('created', newOrder); // ✅
emitter.emit('synced'); // ✅ no second arg needed
// emitter.emit('created'); // ❌ TypeScript error: missing Order
Builder Pattern with Generic Chaining
// Type-safe query builder
type QueryState = {
table: string | undefined;
conditions: string[];
limit: number | undefined;
orderBy: string | undefined;
};
type AddTable<S extends QueryState, T extends string> =
Omit<S, 'table'> & { table: T };
class QueryBuilder<S extends QueryState = { table: undefined; conditions: []; limit: undefined; orderBy: undefined }> {
private state: S;
private constructor(state: S) {
this.state = state;
}
static create() {
return new QueryBuilder({
table: undefined,
conditions: [] as string[],
limit: undefined,
orderBy: undefined,
});
}
from<T extends string>(table: T): QueryBuilder<AddTable<S, T>> {
return new QueryBuilder({ ...this.state, table } as unknown as AddTable<S, T>);
}
where(condition: string): QueryBuilder<S> {
return new QueryBuilder({ ...this.state, conditions: [...this.state.conditions, condition] });
}
// Build() only available when table is defined (not undefined)
build(this: QueryBuilder<S & { table: string }>): string {
const where = this.state.conditions.length ? `WHERE ${this.state.conditions.join(' AND ')}` : '';
return `SELECT * FROM ${this.state.table} ${where}`.trim();
}
}
const query = QueryBuilder.create()
.from('orders')
.where('status = pending')
.build(); // ✅
// QueryBuilder.create().build(); // ❌ TypeScript compile error
For the TypeScript patterns in React components that use these generic types, see the React hooks guide for typed hook patterns. For using TypeScript generics with Zod schema inference, the Pydantic v2 guide shows the Python equivalent and the Zod guide covers TypeScript schema inference. The Claude Skills 360 bundle includes TypeScript generics skill sets covering conditional types, mapped types, and type-safe patterns. Start with the free tier to try generic utility type generation.