# React Native та TypeScript у 2026: Типобезпечна архітектура та питання на співбесіді > Повний гід з типобезпечної розробки React Native у 2026 році: Strict TypeScript API, типізація навігації, TurboModules та Codegen для мобільної архітектури корпоративного рівня - 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 у 2026 році демонструє радикальну трансформацію підходів до типобезпеки. Новий Strict TypeScript API, вбудована підтримка Codegen та обов'язкова типізація TurboModules перетворили екосистему на еталон типобезпечної мобільної розробки. Команди, які досі покладаються на застарілі підходи до типізації Native Modules або ігнорують можливості discriminated unions для управління станом екранів, стикаються з проблемами масштабованості архітектури. Розробники, які опановують сучасні паттерни типобезпечної навігації, генеричні API-хуки та автоматичну кодогенерацію bridge-коду, отримують значні конкурентні переваги на співбесідах та в промислових проєктах. > **Зміни TypeScript у React Native 2026** > > React Native 0.77+ (2026) постачається з обов'язковим TypeScript-first підходом: шаблони проєктів містять `strict: true` за замовчуванням, CLI генерує типізовані TurboModule specs автоматично, а React Navigation 7 вимагає явної типізації всіх параметрів маршрутів без можливості обходу через `any`. Епоха слабко типізованих Native Modules завершилася. ## Чому TypeScript став стандартом для React Native Перехід від JavaScript до TypeScript у React Native відбувся не через модні тренди, а через критичні архітектурні вимоги. Нова архітектура React Native з синхронним JSI bridge вимагає гарантованої узгодженості типів між JavaScript та нативним кодом на етапі компіляції. Помилки типізації параметрів Native Module, які раніше виявлялися лише у runtime на пристроях користувачів, тепер запобігаються Codegen на етапі збірки. Компанії з великими мобільними командами фіксують критичні метрики: кодові бази з повною TypeScript-покриттям показують на 67% менше runtime crashes, пов'язаних з bridge communication, та на 43% швидшу адаптацію нових розробників завдяки самодокументуючим типам навігації та API. Інвестиція у строгу типізацію окупається вже на проєктах від 15+ екранів з нетривіальною нативною функціональністю. TypeScript у React Native 2026 забезпечує безпеку на трьох критичних рівнях: типізацію навігаційних параметрів між екранами, автоматичну генерацію bridge-коду для TurboModules з гарантованою узгодженістю JS/Kotlin/Swift, та end-to-end type safety від API запитів до UI рендерингу через генеричні хуки та discriminated unions. ## Налаштування Strict TypeScript API Strict режим TypeScript у React Native вимагає явної конфігурації, яка виходить за межі базового `strict: true`. Три додаткові опції перетворюють стандартну перевірку типів на корпоративний рівень безпеки. ```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" } ``` Прапорець `exactOptionalPropertyTypes` запобігає прихованим помилкам у навігаційних параметрах: розробник не може передати `undefined` явно у властивість, позначену як optional (`category?: string`). Це критично для екранів з необов'язковими фільтрами, де відсутність параметра та його явне обнулення мають різну семантику. Опція `noUncheckedIndexedAccess` змушує обробляти всі індексні звернення як потенційно `undefined`: `params[key]` має тип `T | undefined`, а не `T`. Це усуває цілий клас runtime crashes при роботі з динамічними route params або Redux state slices з необов'язковими полями. Включення `react-native/types/strict` активує розширені типи для нативних API: методи, що раніше повертали `any` (як AsyncStorage або NetInfo), тепер мають точні generic-сигнатури, які пробрасують типи через Promise chains без втрати інформації. ## Типобезпечна навігація з React Navigation 7 React Navigation 7 вводить обов'язкову типізацію всіх навігаційних графів через глобальний namespace. Система типів автоматично виводить доступні маршрути та їхні параметри для всіх хуків навігації у всьому застосунку. ```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; }; ``` Оголошення типу `undefined` для маршруту без параметрів не є формальністю: TypeScript відкине спробу передати будь-які дані у `navigation.navigate('Home', { ... })` на етапі компіляції. Для маршрутів з обов'язковими параметрами виклик без об'єкта параметрів або з неповним набором полів призведе до помилки збірки. Літеральний union type `'feed' | 'search'` для параметра `source` усуває магічні рядки: спроба передати `'list'` або `'home'` буде відхилена компілятором. Це створює самодокументуючий контракт між екранами без необхідності runtime валідації. ```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 ( ); } ``` Деструктуризація `route.params` без перевірок на `null` або `undefined` стає безпечною: система типів гарантує наявність об'єкта з обов'язковими полями `articleId` та `source`. Спроби звернення до неіснуючих параметрів або використання параметра з неправильним типом виявляються IDE миттєво. > **Типізація useNavigation hook** > > Хук `useNavigation>()` автоматично виводить всі доступні маршрути та їхні параметри без необхідності імпортувати Props екрана. Для nested navigators використовується `CompositeNavigationProp, StackNavigationProp>` для об'єднання типів кількох навігаторів у одному компоненті. ## Побудова типобезпечних TurboModule з Codegen TurboModules у 2026 році виключають можливість ручного написання bridge-коду: розробник створює TypeScript spec, Codegen автоматично генерує C++ JSI binding, Kotlin/Swift протоколи та TypeScript-інтерфейс для JavaScript. Помилки у сигнатурах методів стають неможливими. ```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'); ``` Метод `getDeviceModel()` без `Promise` генерує синхронний JSI binding: виклик з JavaScript блокує JS thread до повернення значення з нативного коду. Це прийнятно для швидких операцій (читання констант, простих обчислень), але критично уникати для IO-операцій. Складний return type `getStorageInfo()` автоматично транслюється у Kotlin data class та Swift struct з ідентичними полями. Codegen гарантує, що нативна реалізація не зможе повернути об'єкт з відсутнім полем `usedPercentage` або з полем `totalBytes: string` замість `number`. Callback parameter у `onBatteryChange` генерує type-safe listener pattern: нативний код отримує Kotlin функцію `(Double) -> Unit`, яка автоматично перетворюється у JavaScript callback з гарантованим типом `number`. Передача неправильного типу з нативного коду у callback призведе до compile error на стороні Kotlin. ```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) } } ``` Базовий клас `NativeDeviceInfoSpec` згенерований Codegen автоматично: він містить абстрактні методи з точними сигнатурами з TypeScript spec. Kotlin компілятор відхилить реалізацію, яка повертає `Int` замість `String` у `getDeviceModel()` або забуває повернути `Promise` у `getBatteryLevel()`. ## Генеричні запити даних з типізованими API-хуками Абстракція над TanStack Query через generic wrapper усуває дублювання типів у кожному API-виклику та забезпечує узгоджену обробку помилок і метаданих пагінації по всьому застосунку. ```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 ``` Generic параметр `T` пробрасується через весь ланцюжок типів: `useQuery` знає, що `queryFn` повертає `Promise>`, тому `data` автоматично виводиться як `ApiResponse | undefined`. Розробник отримує автодоповнення для `data.data[0].title` без ручних type assertions. `Omit>, 'queryKey' | 'queryFn'>` дозволяє передавати додаткові опції (`staleTime`, `refetchInterval`) з повною типізацією, але запобігає перевизначенню `queryKey` та `queryFn`, які керуються wrapper-логікою. Тип `readonly string[]` для query key забезпечує immutability: TanStack Query використовує referential equality для cache invalidation, і випадкова мутація array викликає неконтрольовані refetch. TypeScript запобігає `key.push(...)` на етапі компіляції. ## Discriminated unions для state machines Типізація станів екрана через discriminated unions перетворює switch-case на exhaustive type guard: TypeScript гарантує обробку всіх можливих варіантів і автоматично звужує типи у кожній гілці без runtime перевірок. ```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); } } ``` Discriminator field `status` дозволяє TypeScript автоматично звузити union type у кожному case: всередині `case 'error'` компілятор знає, що `state` має структуру `{ status: 'error'; error: string; retryCount: number }`, тому звернення до `state.error` типобезпечне, а спроба використати `state.data` буде відхилена. Додавання нового стану (наприклад, `{ status: 'refreshing'; data: T }`) автоматично спричинить compile error у всіх switch statements без exhaustive cases: розробник змушений обробити новий варіант або додати `default` clause. Це усуває забуті edge cases. > **Уникайте часткових об'єктів стану** > > Анти-паттерн `{ isLoading: boolean; error: string | null; data: T | null }` створює 8 можливих комбінацій полів, з яких лише 4 валідні. Discriminated unions обмежують простір станів до логічно можливих варіантів: `loading` не може мати `data`, а `loaded` не може мати `error`. Це усуває неможливі runtime стани на рівні типів. ## Питання на співбесіді **Питання 1: Яку проблему вирішує Codegen у TurboModules порівняно з ручним bridge-кодом у старих Native Modules?** Старі Native Modules вимагали ручного написання bridge-коду на трьох мовах: JavaScript інтерфейс з `NativeModules.MyModule`, Objective-C/Swift реалізація з RCT_EXPORT_METHOD макросами, та Java/Kotlin з `@ReactMethod` анотаціями. Узгодженість типів та назв методів між цими трьома шарами забезпечувалася лише документацією та runtime тестуванням. Зміна сигнатури методу вимагала синхронного оновлення всіх трьох файлів. Codegen для TurboModules генерує весь bridge-код автоматично з єдиного TypeScript spec: C++ JSI binding, Kotlin/Swift протоколи, та JavaScript-інтерфейс створюються на етапі збірки. Помилки узгодженості типів між JS та нативним кодом стають compile-time errors замість runtime crashes. Рефакторинг методу потребує зміни лише у spec файлі, решта коду регенерується автоматично. **Питання 2: Навіщо потрібна опція exactOptionalPropertyTypes у strict TypeScript конфігурації?** Без `exactOptionalPropertyTypes` TypeScript дозволяє явно присвоїти `undefined` у optional property: `{ category?: string }` приймає як `{}`, так і `{ category: undefined }`. Для навігаційних параметрів це створює прихований баг: екран з логікою `params.category ?? 'default'` отримує `undefined` замість застосування дефолтного значення, якщо попередній екран передав `{ category: undefined }`. З увімкненою опцією `{ category?: string }` приймає лише об'єкти без поля `category` або з явним string значенням. Спроба передати `{ category: undefined }` призводить до compile error. Це забезпечує коректну семантику optional parameters: відсутність поля та його явне обнулення розрізняються на рівні типів. **Питання 3: Як React Navigation 7 забезпечує type safety для useNavigation hook без Props екрана?** React Navigation 7 використовує TypeScript declaration merging через глобальний namespace `ReactNavigation`. Розробник оголошує інтерфейс `RootParamList` у своєму коді, який автоматично мержиться з внутрішніми типами бібліотеки. Хук `useNavigation()` без generic параметрів автоматично витягує типи з глобального namespace. Всередині будь-якого компонента виклик `const navigation = useNavigation()` отримує повну типізацію всіх маршрутів та їхніх параметрів. Метод `navigation.navigate('Profile', { userId: '123' })` перевіряється на відповідність оголошеному `RootParamList['Profile']` без необхідності імпортувати Props екрана чи передавати generic параметр у хук. Це працює через type inference з глобального scope. **Питання 4: Чому discriminated unions кращі за boolean flags для станів екрана?** Підхід з boolean flags створює об'єкт стану `{ isLoading: boolean; isError: boolean; data: T | null }`, який має 8 можливих комбінацій значень: `{true, true, null}`, `{false, true, data}`, тощо. Більшість комбінацій логічно неможливі (екран не може бути одночасно loading та error), але TypeScript не може це виразити. Розробник змушений додавати runtime перевірки для виключення неможливих станів. Discriminated union `{ status: 'loading' } | { status: 'error'; message: string } | { status: 'loaded'; data: T }` обмежує простір станів до 3 валідних варіантів. TypeScript автоматично звужує тип у switch statement: всередині `case 'loaded'` поле `data` гарантовано присутнє та має тип `T`, а спроби звернутися до `message` відхиляються компілятором. Неможливі стани не можуть бути створені на рівні типів. **Питання 5: Яка різниця між синхронними та асинхронними методами TurboModule з точки зору JSI?** Синхронний метод TurboModule (return type без `Promise`) генерує JSI binding, який блокує JavaScript thread до повернення значення з нативного коду. Виклик `DeviceInfo.getDeviceModel()` зупиняє виконання JS, викликає Kotlin/Swift метод у тому ж потоці (через JSI synchronous call), та повертає результат безпосередньо. Це прийнятно для швидких операцій (читання констант, математичних обчислень), але блокує UI при тривалих операціях. Асинхронний метод (return type `Promise`) генерує JSI binding, який негайно повертає Promise у JavaScript та виконує нативний код у background thread. Виклик `DeviceInfo.getBatteryLevel()` не блокує JS thread: Promise резолвиться після завершення нативної операції. Це обов'язково для IO-операцій (мережа, файлова система, БД), щоб уникнути заморожування UI. **Питання 6: Як noUncheckedIndexedAccess запобігає runtime crashes при роботі з навігаційними параметрами?** Без `noUncheckedIndexedAccess` TypeScript виводить тип `params[key]` як `T`, навіть якщо `key` може не існувати у об'єкті. Для динамічного звернення до route params через змінну це створює баг: `const key = 'category'; const value = params[key]` отримує тип `string`, хоча runtime значення може бути `undefined`. Спроба викликати `value.toUpperCase()` призводить до crash. З увімкненою опцією TypeScript виводить `params[key]` як `T | undefined`, змушуючи розробника додати перевірку: `if (value !== undefined) { value.toUpperCase() }` або використати optional chaining `value?.toUpperCase()`. Це усуває цілий клас null pointer exceptions при роботі з dynamic keys у route params, Redux state, або AsyncStorage. ## Висновки Типобезпечна архітектура React Native у 2026 році базується на кількох критичних принципах: - **Strict TypeScript API** з `exactOptionalPropertyTypes` та `noUncheckedIndexedAccess` усуває цілі класи runtime помилок на етапі компіляції, особливо у навігаційних параметрах та динамічних зверненнях до об'єктів. - **Codegen для TurboModules** автоматично генерує bridge-код з TypeScript specs, гарантуючи узгодженість типів між JavaScript та Kotlin/Swift без можливості помилок у ручному коді. - **Типізація React Navigation 7** через глобальний namespace забезпечує автоматичний type inference для всіх хуків навігації без необхідності передавати Props або generic параметри у кожному компоненті. - **Генеричні API-хуки** пробрасують типи через весь ланцюжок від fetch до UI, усуваючи необхідність у ручних type assertions та забезпечуючи автодоповнення для вкладених полів response. - **Discriminated unions** обмежують простір станів екрана до логічно можливих варіантів, автоматично звужують типи у switch statements та запобігають забутім edge cases через exhaustiveness checking. Команди, які інвестують у повну TypeScript-покриття з першого дня проєкту, фіксують радикальне зниження production crashes, пов'язаних з bridge communication та навігацією. Розробники, які демонструють розуміння Codegen workflow, discriminated unions та strict TypeScript config на співбесідах, отримують значні переваги у конкурентній боротьбі за senior mobile позиції. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/react-native/react-native-typescript-type-safe-architecture