Advanced TypeScript for Architecture
📖 Concept
TypeScript at the Staff level goes beyond basic type annotations. You're defining type systems that enforce architectural invariants at compile time — preventing entire categories of bugs before code even runs.
Key advanced patterns for React Native architecture:
- Generic Constraints — Ensuring type relationships between function parameters and return types
- Conditional Types — Types that change based on input types (
T extends U ? X : Y) - Mapped Types — Transforming existing types systematically
- Template Literal Types — String manipulation at the type level
- Discriminated Unions — Type-safe state machines (critical for RN navigation and state)
- Type Predicates — Custom type guards for runtime narrowing
- Branded/Nominal Types — Preventing primitive confusion (UserId vs PostId)
- Utility Types Mastery —
Partial,Required,Pick,Omit,Record,Extract,Exclude,ReturnType,Parameters,Awaited
Architectural impact of good TypeScript:
- API contracts are enforced at build time — no runtime type mismatch crashes
- Navigation parameters are type-safe — can't navigate to a screen with wrong params
- Redux actions and state are exhaustively typed — impossible to dispatch a wrong action
- Component props contracts prevent integration bugs between teams
- Refactoring is safe — the compiler catches breaking changes across 200K LOC
💻 Code Example
1// === ADVANCED TYPESCRIPT FOR RN ARCHITECTURE ===23// 1. Branded Types — prevent ID confusion4type Brand<T, B> = T & { __brand: B };5type UserId = Brand<string, 'UserId'>;6type PostId = Brand<string, 'PostId'>;7type OrderId = Brand<string, 'OrderId'>;89function createUserId(id: string): UserId { return id as UserId; }10function createPostId(id: string): PostId { return id as PostId; }1112function fetchUser(id: UserId): Promise<User> { /* ... */ }13function fetchPost(id: PostId): Promise<Post> { /* ... */ }1415const userId = createUserId('u_123');16const postId = createPostId('p_456');1718fetchUser(userId); // ✅19// fetchUser(postId); // ❌ Compile error! Can't pass PostId where UserId expected2021// 2. Discriminated Unions for State Machines22type NetworkState<T> =23 | { status: 'idle' }24 | { status: 'loading' }25 | { status: 'success'; data: T }26 | { status: 'error'; error: Error; retryCount: number };2728// Exhaustive handling — compiler ensures every case is handled29function renderState<T>(state: NetworkState<T>) {30 switch (state.status) {31 case 'idle': return <EmptyState />;32 case 'loading': return <Spinner />;33 case 'success': return <DataView data={state.data} />; // data is typed!34 case 'error': return <ErrorView error={state.error} />;35 // If you add a new status, TypeScript errors until you handle it36 }37}3839// 3. Type-Safe Navigation (React Navigation)40type RootStackParamList = {41 Home: undefined;42 Profile: { userId: UserId };43 Post: { postId: PostId; showComments?: boolean };44 Settings: undefined;45};4647// Enforces correct params at every navigation call48declare const navigation: NavigationProp<RootStackParamList>;49navigation.navigate('Profile', { userId: createUserId('u_1') }); // ✅50// navigation.navigate('Profile', { postId: 'p_1' }); // ❌ Wrong params!51// navigation.navigate('Profle'); // ❌ Typo caught at compile time!5253// 4. Generic API Layer54interface APIResponse<T> {55 data: T;56 meta: { page: number; total: number };57}5859interface APIEndpoints {60 '/users': { list: User[]; get: User; create: CreateUserDTO };61 '/posts': { list: Post[]; get: Post; create: CreatePostDTO };62 '/orders': { list: Order[]; get: Order; create: CreateOrderDTO };63}6465type EndpointMethod = 'list' | 'get' | 'create';6667async function apiCall<68 E extends keyof APIEndpoints,69 M extends EndpointMethod70>(71 endpoint: E,72 method: M,73 ...args: M extends 'create' ? [APIEndpoints[E][M]] : []74): Promise<APIResponse<APIEndpoints[E][M]>> {75 // Implementation — fully type-safe API calls76}7778// Usage — compiler knows exact return type79const users = await apiCall('/users', 'list'); // APIResponse<User[]>80const post = await apiCall('/posts', 'get'); // APIResponse<Post>81// await apiCall('/users', 'delete'); // ❌ 'delete' not in EndpointMethod8283// 5. Conditional Types for Platform-Specific Code84type PlatformSpecific<IOS, Android> =85 typeof Platform.OS extends 'ios' ? IOS : Android;8687type HapticFeedback = PlatformSpecific<IOSHaptic, AndroidVibration>;8889// 6. Utility Type Composition90type DeepPartial<T> = {91 [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];92};9394type DeepRequired<T> = {95 [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];96};9798type DeepReadonly<T> = {99 readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];100};101102// Use for configuration objects that are partially overridable103type AppConfig = DeepRequired<{104 api: { baseUrl: string; timeout: number; retries: number };105 features: { darkMode: boolean; analytics: boolean };106 cache: { maxSize: number; ttl: number };107}>;108109type UserOverrides = DeepPartial<AppConfig>;
🏋️ Practice Exercise
TypeScript Architecture Exercises:
- Define a type-safe event emitter where event names and payloads are statically typed
- Create a
createAction/createReducerpattern where the compiler ensures exhaustive action handling - Implement a type-safe
useQueryhook where the return type is inferred from the query function - Define branded types for all ID types in your project — enforce them across the codebase
- Create a type-safe form validation system where field validators are typed to match the form schema
- Build a pipeline/compose function with correct type inference for each step
⚠️ Common Mistakes
Using
anyto 'fix' complex type errors — this defeats the entire purpose of TypeScript and hides real bugsOver-engineering types — if a type definition is harder to understand than the code it protects, simplify it
Not using discriminated unions for state — using boolean flags (isLoading, isError, hasData) creates impossible states
Forgetting that TypeScript types are erased at runtime — you still need runtime validation for external data (API responses, user input)
Not leveraging generics for shared utilities — duplicating similar types instead of creating generic abstractions
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Advanced TypeScript for Architecture. Login to unlock this feature.