React Native та TypeScript у 2026: Типобезпечна архітектура та питання співбесіди

Створення типобезпечних React Native додатків з TypeScript, Codegen, TurboModules та Strict TypeScript API. Охоплює патерни архітектури, типізовану навігацію, вимоги toolchain для 0.87 та питання співбесіди.

Діаграма архітектури React Native з TypeScript, що показує типобезпечні потоки даних та TurboModules

Типобезпека React Native TypeScript значно вдосконалилася до середини 2026 року, а версія 0.87 зробила Strict TypeScript API обов'язковим за замовчуванням і завершила масштабну модернізацію toolchain. TypeScript більше не є опціональним клеєм між JavaScript та native-кодом: він контролює весь контракт від пропсів компонентів до інтерфейсів TurboModule, виявляючи невідповідності на етапі збірки, а не в логах збоїв продакшену.

Що змінилося в 0.87

React Native 0.87 (серпень 2026) робить Strict TypeScript API обов'язковим за замовчуванням. Також підвищуються мінімальні вимоги до Node.js 22.13+, Kotlin 2.0+ та Android Gradle Plugin 9. Можливість відключення для застарілих deep imports залишається доступною лише до версії 0.88.

Чому TypeScript став стандартом для проєктів React Native

Кожен виклик npx react-native init з версії 0.76 створює TypeScript-проєкт. Але справжній зсув відбувся на межі з native-кодом. До Codegen розробники писали ручні type assertions при переході з JavaScript до Objective-C або Kotlin, що було слабко типізованим контрактом, який мовчки ламався в runtime. Codegen читає файли специфікацій TypeScript і автоматично генерує інтерфейси C++, Objective-C++ та Java/Kotlin. Якщо специфікація TypeScript оголошує метод, що повертає number, згенерований native-інтерфейс забезпечує це обмеження на етапі компіляції.

Документація React Native охоплює базове налаштування, але патерни типобезпеки, важливі для продакшену, йдуть далі: типізовані стеки навігації, generic API hooks, discriminated unions для state machines та специфікації TurboModule через Codegen.

Strict TypeScript API: від опції до стандарту

Strict TypeScript API було представлено в React Native 0.80 як opt-in функція. З версії 0.87 це обов'язковий стандарт для всіх нових проєктів. Типи генеруються безпосередньо з вихідного коду React Native замість ручного супроводу, усуваючи розбіжності між задокументованим API та фактичною реалізацією.

tsconfig.json (стандартна конфігурація 0.87+)json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

З цією конфігурацією імпорт чогось із підшляху на кшталт react-native/Libraries/Text/Text викликає помилку типу. Всі імпорти мають надходити з кореневого пакета react-native. Якщо бібліотека або застаріла кодова база все ще покладається на deep imports, тимчасове відключення доступне до версії 0.88:

json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "customConditions": ["react-native", "react-native-legacy-deep-imports"]
  }
}

Це відключення буде видалено в 0.89. Міграцію слід виконати до того часу.

Вимоги toolchain у 0.87

React Native 0.87 підвищує мінімальні версії по всьому ланцюгу збірки:

КомпонентМінімум
Node.js22.13.0
Kotlin2.0 (включена: 2.2.0)
Android Gradle Plugin9.x
Android compileSdk34 (мін), 37 (target)

Для Android AGP 9 потребує додаткової конфігурації:

properties
# android/gradle.properties
android.builtInKotlin=false
android.newDsl=false

Ці вимоги стосуються всіх проєктів, що цілять на 0.87+. Посібник міграції на New Architecture охоплює ширший контекст цих змін.

Типобезпечна навігація з React Navigation 7

React Navigation 7.x забезпечує першокласну підтримку TypeScript. Ключовий патерн: визначити тип RootStackParamList, який відображає кожну назву екрана на очікувані параметри, а потім прокинути цей тип через навігатори та компоненти екранів.

navigation/types.tstypescript
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;
};

Компоненти екранів отримують типізовані пропси без ручного приведення:

screens/ArticleDetailScreen.tsxtypescript
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../navigation/types';

type Props = NativeStackScreenProps<RootStackParamList, 'ArticleDetail'>;

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' }) is type-checked
  // navigation.navigate('Profile', {}) causes a compile error: missing userId
  return (
    <ArticleView id={articleId} referrer={source} />
  );
}

Це усуває цілий клас помилок runtime: навігація до екрана з неправильними або відсутніми параметрами завершується помилкою на етапі збірки.

Типізація хука useNavigation

Для компонентів, що не є прямими дочірніми елементами екрана, використання useNavigation<NativeStackNavigationProp<RootStackParamList>>() забезпечує таку саму типобезпеку без prop drilling.

Створення типобезпечних TurboModule з Codegen

TurboModules замінюють стару систему Native Modules. Файл специфікації TypeScript слугує єдиним джерелом істини, а Codegen генерує з нього native-інтерфейси. Якщо специфікація і native-реалізація розходяться, збірка завершується помилкою.

specs/NativeDeviceInfo.tstypescript
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  getDeviceModel(): string;
  getBatteryLevel(): Promise<number>;
  getStorageInfo(): Promise<{
    totalBytes: number;
    freeBytes: number;
    usedPercentage: number;
  }>;
  onBatteryChange(callback: (level: number) => void): void;
}

export default TurboModuleRegistry.getEnforcing<Spec>('DeviceInfo');

Виконання npx react-native codegen генерує відповідні інтерфейси C++, Objective-C++ та Java. Native-реалізація має точно відповідати кожній сигнатурі методу. Наприклад, getStorageInfo має повертати об'єкт з трьома числовими полями; повернення іншої форми викликає помилку компіляції на native-стороні.

android/app/src/main/java/com/app/DeviceInfoModule.ktkotlin
class DeviceInfoModule(reactContext: ReactApplicationContext) :
    NativeDeviceInfoSpec(reactContext) {

    // Return type enforced by generated NativeDeviceInfoSpec
    override fun getDeviceModel(): String {
        return Build.MODEL
    }

    override fun getBatteryLevel(): Promise<Double> {
        val bm = reactContext.getSystemService(Context.BATTERY_SERVICE)
            as BatteryManager
        val level = bm.getIntProperty(
            BatteryManager.BATTERY_PROPERTY_CAPACITY
        ).toDouble()
        return Promise.resolve(level)
    }
}

Цей підхід усуває парсинг ReadableMap та NSDictionary, який спричиняв мовчазні помилки приведення типів у старій архітектурі. Глибший огляд системи native-модулів доступний у питаннях співбесіди про native modules.

Готовий до співбесід з React Native?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Generic завантаження даних з типізованими API-хуками

Патерн багаторазового типізованого хука дозволяє уникнути дублювання логіки fetch між екранами, зберігаючи повну інференцію типів:

hooks/useApiQuery.tstypescript
import { useQuery, UseQueryOptions } from '@tanstack/react-query';

interface ApiResponse<T> {
  data: T;
  meta: { page: number; totalPages: number };
}

export function useApiQuery<T>(
  key: readonly string[],
  endpoint: string,
  options?: Omit<UseQueryOptions<ApiResponse<T>>, 'queryKey' | 'queryFn'>
) {
  return useQuery<ApiResponse<T>>({
    queryKey: key,
    queryFn: async () => {
      const response = await fetch(`${API_BASE}${endpoint}`);
      if (!response.ok) throw new ApiError(response.status);
      return response.json() as Promise<ApiResponse<T>>;
    },
    ...options,
  });
}

// Usage, T is inferred as Article[]
interface Article {
  id: string;
  title: string;
  publishedAt: string;
}

const { data, isLoading } = useApiQuery<Article[]>(
  ['articles', 'latest'],
  '/articles?sort=latest'
);
// data.data is Article[], fully typed
// data.meta.totalPages is number

Generic-параметр T протікає через весь ланцюг: від місця виклику хука, через функцію запиту, до компонента, що споживає результат. Жодних приведень as, жодних типів any.

Discriminated unions для state machines

Складні стани екрана (loading, error, empty, loaded) найкраще моделювати як discriminated unions замість набору опціональних полів:

types/screen-state.tstypescript
type ScreenState<T> =
  | { 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<T>(state: ScreenState<T>, renderItem: (data: T) => ReactNode) {
  switch (state.status) {
    case 'idle':
      return null;
    case 'loading':
      return <LoadingSpinner />;
    case 'error':
      // state.error is string here, TypeScript narrows automatically
      return <ErrorBanner message={state.error} retries={state.retryCount} />;
    case 'empty':
      return <EmptyState message={state.message} />;
    case 'loaded':
      // state.data is T, fully typed
      return renderItem(state.data);
  }
}

Цей патерн робить неможливі стани непредставними. Стан loading не може випадково містити застарілі data, а стан error завжди включає контекст для відлагодження.

Уникайте часткових об'єктів стану

Поширений антипатерн: { isLoading: boolean; error?: string; data?: T }. Це дозволяє стани на кшталт { isLoading: true, error: 'fail', data: [...] }, три суперечливі сигнали одночасно. Discriminated unions запобігають цьому на рівні типів.

Питання співбесіди React Native TypeScript

Ці питання відображають те, що запитують команди senior mobile engineering на співбесідах у 2026 році, коли New Architecture та TypeScript є стандартом.

Як Codegen забезпечує типобезпеку на межі JavaScript-native?

Codegen читає файли специфікацій TypeScript (або Flow) і генерує код інтерфейсів C++, Objective-C++ та Java/Kotlin. Згенеровані native-інтерфейси забезпечують точні сигнатури методів, типи параметрів і типи повернення, визначені в специфікації. Якщо native-реалізація відхиляється (повертає Int де специфікація оголошує Double, або пропускає поле зі структури), native-компілятор відхиляє збірку. Це переносить помилки типів з runtime-збоїв до помилок збірки.

Що таке Strict TypeScript API і чому воно важливе?

Strict TypeScript API генерує типи безпосередньо з вихідного коду React Native замість супроводу вручну написаних файлів .d.ts. У версії 0.87 воно стало обов'язковим за замовчуванням. Воно обмежує імпорти до кореневого пакета react-native, deprecate'ячи deep imports. Це визначає стабільну публічну API-поверхню: внутрішні рефакторинги не можуть зламати споживчий код, якщо споживачі використовують лише strict types. Тимчасове відключення через customConditions залишається доступним до версії 0.88.

Як типізувати параметри React Navigation між вкладеними навігаторами?

Визначається тип ParamList для кожного навігатора і вони композуються за допомогою NavigatorScreenParams. Для tab-навігатора, вкладеного в stack, param list stack'а посилається на tab: type RootStack = { Main: NavigatorScreenParams<TabParamList>; Modal: { id: string } }. Кожен виклик navigate() перевіряється на типи через всю ієрархію вкладеності, виявляючи неправильні назви екранів або відсутні параметри на етапі компіляції.

Яку проблему вирішують discriminated unions в управлінні станом React Native?

Discriminated unions моделюють взаємовиключні стани (loading, error, loaded) як окремі гілки типу union, ключовані полем status. TypeScript звужує тип у кожній гілці оператора switch, тому доступ до state.data можливий лише коли state.status === 'loaded'. Це запобігає неможливим станам, як-от показ індикатора завантаження поряд з даними помилки, клас помилок, який опціональні поля та boolean-прапорці не можуть запобігти.

Поясніть різницю між TurboModules та старою системою Native Modules.

Native Modules спілкувалися через асинхронний bridge, серіалізуючи всі дані в JSON. TurboModules використовують JSI (JavaScript Interface) для синхронних, прямих викликів C++ без накладних витрат серіалізації. Вони також завантажуються ліниво (при першому використанні замість запуску додатку, зменшуючи час холодного старту) і використовують Codegen для генерації типобезпечних інтерфейсів зі специфікацій TypeScript. Стара система покладалася на парсинг ReadableMap / NSDictionary з приведенням типів у runtime; TurboModules забезпечують типи на етапі компіляції.

Які API були видалені в React Native 0.87?

Версія 0.87 видалила InteractionManager (використовуйте requestIdleCallback), типи NativeMethods / NativeMethodsMixin (використовуйте HostInstance) та кілька пропсів StatusBar. Deep imports до шляхів src/private/ тепер є помилками типів. Прапорець useTurboModules також видалено, оскільки TurboModules тепер завжди увімкнені.

Більше питань співбесіди React Native у повному посібнику, що охоплює архітектуру, продуктивність та відлагодження.

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Джерела

Що запам'ятати про React Native TypeScript у 2026

  • Strict TypeScript API (обов'язкове в 0.87) обмежує імпорти стабільною публічною поверхнею, запобігаючи поломкам через внутрішні зміни. Відключення для застарілих deep imports доступне лише до версії 0.88
  • Codegen генерує native-інтерфейси з файлів специфікацій TypeScript, переносячи помилки типів з runtime-збоїв до помилок збірки через межу JS-native
  • Мінімальні вимоги toolchain у 0.87: Node.js 22.13+, Kotlin 2.0+, AGP 9, compileSdk 34+
  • Типізовані параметри навігації через RootStackParamList та NativeStackScreenProps виявляють неправильні назви екранів та відсутні параметри до запуску додатку
  • Discriminated unions моделюють стани екрана як взаємовиключні гілки, роблячи неможливі стани непредставними на рівні типів
  • TurboModules з типізованими специфікаціями замінюють старий парсинг ReadableMap / NSDictionary, забезпечуючи повну типобезпеку від JavaScript через C++ до platform-native коду
  • Generic API-хуки з TanStack Query зберігають інференцію типів від endpoint до компонента без ручних приведень

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Щоденний виклик

Чи знайдеш ти помилку в React Native?

Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Anthony Fillion-Maillet

Автор:

Anthony Fillion-Maillet

Засновник SharpSkill

Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.

Оновлено 25 серпня 2026 р.

Теги

#react-native
#typescript
#mobile-development
#new-architecture
#turbomodules

Поділитися

Пов'язані статті