# React Native and TypeScript in 2026: Type-Safe Architecture and Interview Questions > Build type-safe React Native apps with TypeScript, Codegen, TurboModules, and the Strict TypeScript API. Covers architecture patterns, typed navigation, and interview questions for 2026. - Published: 2026-06-16 - Updated: 2026-06-16 - Author: SharpSkill - Tags: react-native, typescript, mobile-development, new-architecture, turbomodules - Reading time: 9 min --- React Native TypeScript type safety has matured significantly by mid-2026, with version 0.86 shipping React 19.1, the Strict TypeScript API, and a fully bridgeless architecture powered by Codegen. TypeScript is no longer optional glue between JavaScript and native code — it drives the entire contract from component props to TurboModule interfaces, catching mismatches at build time rather than in production crash logs. > **What changed in 2026** > > React Native 0.80 introduced the opt-in Strict TypeScript API, 0.82 permanently removed the legacy bridge, and 0.85 dropped the last interop layer. As of 0.86 (June 2026), every new project starts fully bridgeless with TypeScript types generated directly from source code. ## Why TypeScript Is Now the Default for React Native Projects Every `npx react-native init` invocation since version 0.76 scaffolds a TypeScript project. But the real shift happened at the native boundary. Before Codegen, developers wrote manual type assertions when crossing from JavaScript to Objective-C or Kotlin — a stringly-typed contract that broke silently at runtime. Codegen reads TypeScript spec files and generates C++, Objective-C++, and Java/Kotlin interfaces automatically. If the TypeScript spec declares a method returning `number`, the generated native interface enforces that constraint at compile time. The [React Native documentation](https://reactnative.dev/docs/typescript) covers the baseline setup, but the type-safe patterns that matter in production go further: typed navigation stacks, generic API hooks, discriminated unions for state machines, and Codegen-driven TurboModule specs. ## Setting Up the Strict TypeScript API The Strict TypeScript API, introduced in React Native 0.80, restricts the public API surface to types generated directly from source code. This prevents accidental reliance on internal modules that could break between minor versions. ```json // tsconfig.json { "compilerOptions": { "strict": true, "exactOptionalPropertyTypes": true, "noUncheckedIndexedAccess": true, "moduleResolution": "bundler", "jsx": "react-jsx", "types": ["react-native/types/strict"] }, "extends": "@react-native/typescript-config/tsconfig.json" } ``` With this configuration, importing anything from a subpath like `react-native/Libraries/Text/Text` triggers a type error. All imports must come from the root `react-native` package, aligning with the deep-import deprecation enforced since 0.80. ## Type-Safe Navigation with React Navigation 7 [React Navigation 7.x](https://reactnavigation.org/docs/typescript/) provides first-class TypeScript support. The key pattern: define a `RootStackParamList` type that maps every screen name to its expected params, then thread that type through navigators and screen components. ```typescript // navigation/types.ts export type RootStackParamList = { Home: undefined; Profile: { userId: string }; Settings: undefined; ArticleDetail: { articleId: string; source: 'feed' | 'search' }; }; export type AppTabParamList = { Dashboard: undefined; Explore: { category?: string }; Notifications: undefined; }; ``` Screen components then receive typed props without manual casting: ```typescript // screens/ArticleDetailScreen.tsx import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import type { RootStackParamList } from '../navigation/types'; type Props = NativeStackScreenProps; export function ArticleDetailScreen({ route, navigation }: Props) { // route.params.articleId is string — guaranteed by the type // route.params.source is 'feed' | 'search' — no runtime check needed const { articleId, source } = route.params; // navigation.navigate('Profile', { userId: '123' }) — type-checked // navigation.navigate('Profile', {}) — compile error: missing userId return ( ); } ``` This eliminates an entire class of runtime errors — navigating to a screen with wrong or missing params fails at build time. > **useNavigation hook typing** > > For components that aren't direct screen children, use `useNavigation>()` to get the same type safety without prop drilling. ## Building a Type-Safe TurboModule with Codegen TurboModules replace the old Native Modules system. The TypeScript spec file serves as the single source of truth — Codegen generates the native interfaces from it. If the spec and native implementation diverge, the build fails. ```typescript // specs/NativeDeviceInfo.ts import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; export interface Spec extends TurboModule { getDeviceModel(): string; getBatteryLevel(): Promise; getStorageInfo(): Promise<{ totalBytes: number; freeBytes: number; usedPercentage: number; }>; onBatteryChange(callback: (level: number) => void): void; } export default TurboModuleRegistry.getEnforcing('DeviceInfo'); ``` Running `npx react-native codegen` generates the corresponding C++, Objective-C++, and Java interfaces. The native implementation must match every method signature exactly. For example, `getStorageInfo` must return an object with three numeric fields — returning a different shape causes a compile-time error on the native side. ```kotlin // android/app/src/main/java/com/app/DeviceInfoModule.kt class DeviceInfoModule(reactContext: ReactApplicationContext) : NativeDeviceInfoSpec(reactContext) { // Return type enforced by generated NativeDeviceInfoSpec override fun getDeviceModel(): String { return Build.MODEL } override fun getBatteryLevel(): Promise { val bm = reactContext.getSystemService(Context.BATTERY_SERVICE) as BatteryManager val level = bm.getIntProperty( BatteryManager.BATTERY_PROPERTY_CAPACITY ).toDouble() return Promise.resolve(level) } } ``` This approach eliminates the `ReadableMap` and `NSDictionary` parsing that caused silent type coercion bugs in the old architecture. ## Generic Data Fetching with Typed API Hooks A reusable typed hook pattern avoids duplicating fetch logic across screens while preserving full type inference: ```typescript // hooks/useApiQuery.ts import { useQuery, UseQueryOptions } from '@tanstack/react-query'; interface ApiResponse { data: T; meta: { page: number; totalPages: number }; } export function useApiQuery( key: readonly string[], endpoint: string, options?: Omit>, 'queryKey' | 'queryFn'> ) { return useQuery>({ queryKey: key, queryFn: async () => { const response = await fetch(`${API_BASE}${endpoint}`); if (!response.ok) throw new ApiError(response.status); return response.json() as Promise>; }, ...options, }); } // Usage — T is inferred as Article[] interface Article { id: string; title: string; publishedAt: string; } const { data, isLoading } = useApiQuery( ['articles', 'latest'], '/articles?sort=latest' ); // data.data is Article[] — fully typed // data.meta.totalPages is number ``` The generic parameter `T` flows through the entire chain: from the hook call site, through the query function, to the component that consumes the result. No `as` casts, no `any` types. ## Discriminated Unions for State Machines Complex screen states — loading, error, empty, loaded — are best modeled as discriminated unions rather than a bag of optional fields: ```typescript // types/screen-state.ts type ScreenState = | { status: 'idle' } | { status: 'loading' } | { status: 'error'; error: string; retryCount: number } | { status: 'empty'; message: string } | { status: 'loaded'; data: T; refreshedAt: Date }; // components/DataScreen.tsx function renderContent(state: ScreenState, renderItem: (data: T) => ReactNode) { switch (state.status) { case 'idle': return null; case 'loading': return ; case 'error': // state.error is string here — TypeScript narrows automatically return ; case 'empty': return ; case 'loaded': // state.data is T — fully typed return renderItem(state.data); } } ``` This pattern makes impossible states unrepresentable. A `loading` state cannot accidentally carry stale `data`, and an `error` state always includes context for debugging. > **Avoid partial state objects** > > A common anti-pattern: `{ isLoading: boolean; error?: string; data?: T }`. This allows states like `{ isLoading: true, error: 'fail', data: [...] }` — three contradictory signals at once. Discriminated unions prevent this at the type level. ## React Native TypeScript Interview Questions These questions reflect what senior mobile engineering teams ask in 2026 interviews, now that the New Architecture and TypeScript are standard. **How does Codegen enforce type safety across the JavaScript-native boundary?** Codegen reads TypeScript (or Flow) spec files and generates C++, Objective-C++, and Java/Kotlin interface code. The generated native interfaces enforce the exact method signatures, parameter types, and return types defined in the spec. If the native implementation deviates — returning `Int` where the spec declares `Double`, or omitting a field from a struct — the native compiler rejects the build. This moves type errors from runtime crashes to build-time failures. **What is the Strict TypeScript API and why does it matter?** Introduced in React Native 0.80, the Strict TypeScript API generates types directly from React Native's source code rather than maintaining hand-written `.d.ts` files. It restricts imports to the root `react-native` package, deprecating deep imports. This defines a stable public API surface — internal refactors cannot break consumer code if consumers only use strict types. Enable it via `"types": ["react-native/types/strict"]` in `tsconfig.json`. **How do you type React Navigation params across nested navigators?** Define a `ParamList` type per navigator and compose them using `NavigatorScreenParams`. For a tab navigator nested inside a stack, the stack's param list references the tab's: `type RootStack = { Main: NavigatorScreenParams; Modal: { id: string } }`. Every `navigate()` call is then type-checked through the full nesting hierarchy, catching wrong screen names or missing params at compile time. **What problem do discriminated unions solve in React Native state management?** Discriminated unions model mutually exclusive states (loading, error, loaded) as separate branches of a union type, keyed by a `status` field. TypeScript narrows the type in each branch of a switch statement, so accessing `state.data` is only possible when `state.status === 'loaded'`. This prevents impossible states like a loading indicator showing alongside error data — a class of bugs that optional fields and boolean flags cannot prevent. **Explain the difference between TurboModules and the old Native Modules system.** Native Modules communicated over the async bridge, serializing all data to JSON. TurboModules use JSI (JavaScript Interface) for synchronous, direct C++ calls — no serialization overhead. They also load lazily (on first use instead of app startup, reducing cold start time) and use Codegen to generate type-safe interfaces from TypeScript specs. The old system relied on `ReadableMap` / `NSDictionary` parsing with runtime type coercion; TurboModules enforce types at compile time. For more [React Native interview questions](/technologies/react-native/interview-questions/rn-native-modules), the native modules deep-dive covers JSI, Fabric, and bridgeless architecture in detail. ## Conclusion - The Strict TypeScript API (0.80+) restricts imports to the stable public surface, preventing breakage from internal changes — enable it in every new project - Codegen generates native interfaces from TypeScript spec files, moving type errors from runtime crashes to build-time failures across the JS-native boundary - Typed navigation params via `RootStackParamList` and `NativeStackScreenProps` catch wrong screen names and missing params before the app runs - Discriminated unions model screen states as mutually exclusive branches, making impossible states unrepresentable at the type level - TurboModules with typed specs replace the old `ReadableMap` / `NSDictionary` parsing, enforcing full type safety from JavaScript through C++ to platform-native code - Generic API hooks with [TanStack Query](https://tanstack.com/query/latest) preserve type inference from endpoint to component without manual casts --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/en/blog/react-native/react-native-typescript-type-safe-architecture