React Native et TypeScript en 2026 : Architecture Type-Safe et Questions d'Entretien

Construire des applications React Native robustes avec TypeScript, Codegen, TurboModules et l'API Strict TypeScript. Architecture type-safe, navigation typée et questions d'entretien technique pour 2026.

Architecture type-safe React Native TypeScript avec code et appareils mobiles

La sécurité de typage dans React Native a franchi un cap décisif en 2026. Avec la version 0.86 livrée en juin, le framework embarque React 19.1, l'API Strict TypeScript et une architecture entièrement sans bridge alimentée par Codegen. TypeScript ne se contente plus de décorer le code JavaScript — il pilote le contrat complet, des props de composants aux interfaces TurboModule, détectant les incohérences à la compilation plutôt que dans les crash logs en production.

Ce qui a changé en 2026

React Native 0.80 a introduit l'API Strict TypeScript en opt-in, la version 0.82 a supprimé définitivement le bridge legacy, et 0.85 a retiré la dernière couche d'interopérabilité. Depuis la version 0.86 (juin 2026), chaque nouveau projet démarre en mode entièrement bridgeless avec des types TypeScript générés directement depuis le code source.

Pourquoi TypeScript est devenu le standard pour les projets React Native

Chaque invocation de npx react-native init génère un projet TypeScript depuis la version 0.76. Mais le véritable tournant s'est produit à la frontière native. Avant Codegen, les développeurs écrivaient des assertions de type manuelles lors du passage de JavaScript vers Objective-C ou Kotlin — un contrat basé sur des chaînes de caractères qui échouait silencieusement à l'exécution. Codegen lit les fichiers de spécification TypeScript et génère automatiquement les interfaces C++, Objective-C++ et Java/Kotlin. Si la spécification TypeScript déclare une méthode retournant number, l'interface native générée impose cette contrainte à la compilation.

La documentation React Native couvre la configuration de base, mais les patterns type-safe qui comptent en production vont plus loin : piles de navigation typées, hooks API génériques, unions discriminées pour les machines à états, et spécifications TurboModule pilotées par Codegen.

Configurer l'API Strict TypeScript

L'API Strict TypeScript, introduite dans React Native 0.80, restreint la surface de l'API publique aux types générés directement depuis le code source. Cela empêche toute dépendance accidentelle envers des modules internes susceptibles de casser entre versions mineures.

tsconfig.jsonjson
{
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true,
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "types": ["react-native/types/strict"]
  },
  "extends": "@react-native/typescript-config/tsconfig.json"
}

Avec cette configuration, importer quoi que ce soit depuis un sous-chemin comme react-native/Libraries/Text/Text déclenche une erreur de type. Tous les imports doivent provenir du package racine react-native, conformément à la dépréciation des imports profonds appliquée depuis la version 0.80.

React Navigation 7.x offre un support TypeScript natif de premier ordre. Le pattern fondamental : définir un type RootStackParamList qui associe chaque nom d'écran à ses paramètres attendus, puis propager ce type à travers les navigateurs et les composants d'écran.

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;
};

Les composants d'écran reçoivent alors des props typées sans conversion manuelle :

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 est string — garanti par le type
  // route.params.source est 'feed' | 'search' — aucune vérification runtime nécessaire
  const { articleId, source } = route.params;

  // navigation.navigate('Profile', { userId: '123' }) — vérifié par le compilateur
  // navigation.navigate('Profile', {}) — erreur de compilation : userId manquant
  return (
    <ArticleView id={articleId} referrer={source} />
  );
}

Ce mécanisme élimine une classe entière d'erreurs runtime — naviguer vers un écran avec des paramètres incorrects ou manquants échoue à la compilation.

Typage du hook useNavigation

Pour les composants qui ne sont pas des enfants directs d'un écran, utiliser useNavigation<NativeStackNavigationProp<RootStackParamList>>() procure la même sécurité de type sans prop drilling.

Créer un TurboModule Type-Safe avec Codegen

Les TurboModules remplacent l'ancien système de Native Modules. Le fichier de spécification TypeScript constitue la source unique de vérité — Codegen génère les interfaces natives à partir de celui-ci. Si la spécification et l'implémentation native divergent, le build échoue.

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');

L'exécution de npx react-native codegen génère les interfaces correspondantes en C++, Objective-C++ et Java. L'implémentation native doit correspondre exactement à chaque signature de méthode. Par exemple, getStorageInfo doit retourner un objet avec trois champs numériques — retourner une forme différente provoque une erreur de compilation côté natif.

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

    // Le type de retour est imposé par le NativeDeviceInfoSpec généré
    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)
    }
}

Cette approche élimine le parsing de ReadableMap et NSDictionary qui causait des bugs silencieux de coercition de type dans l'ancienne architecture.

Prêt à réussir tes entretiens React Native ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Hooks API Génériques avec Typage Complet

Un pattern de hook typé réutilisable évite la duplication de la logique de fetch entre les écrans tout en préservant l'inférence de type complète :

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,
  });
}

// Utilisation — T est inféré comme Article[]
interface Article {
  id: string;
  title: string;
  publishedAt: string;
}

const { data, isLoading } = useApiQuery<Article[]>(
  ['articles', 'latest'],
  '/articles?sort=latest'
);
// data.data est Article[] — entièrement typé
// data.meta.totalPages est number

Le paramètre générique T se propage dans toute la chaîne : du site d'appel du hook, à travers la fonction de requête, jusqu'au composant qui consomme le résultat. Aucun cast as, aucun type any.

Unions Discriminées pour les Machines à États

Les états d'écran complexes — chargement, erreur, vide, chargé — se modélisent au mieux sous forme d'unions discriminées plutôt que comme un ensemble de champs optionnels :

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 est string ici — TypeScript réduit le type automatiquement
      return <ErrorBanner message={state.error} retries={state.retryCount} />;
    case 'empty':
      return <EmptyState message={state.message} />;
    case 'loaded':
      // state.data est T — entièrement typé
      return renderItem(state.data);
  }
}

Ce pattern rend les états impossibles irreprésentables. Un état loading ne peut pas accidentellement transporter des données obsolètes, et un état error contient toujours le contexte nécessaire au débogage.

Éviter les objets d'état partiels

Un anti-pattern courant : { isLoading: boolean; error?: string; data?: T }. Ce schéma autorise des états comme { isLoading: true, error: 'fail', data: [...] } — trois signaux contradictoires simultanément. Les unions discriminées empêchent cela au niveau du système de types.

Questions d'Entretien React Native TypeScript

Ces questions reflètent ce que les équipes d'ingénierie mobile senior demandent lors des entretiens en 2026, maintenant que la New Architecture et TypeScript sont devenus le standard.

Comment Codegen assure-t-il la sécurité de type à la frontière JavaScript-natif ?

Codegen lit les fichiers de spécification TypeScript (ou Flow) et génère du code d'interface en C++, Objective-C++ et Java/Kotlin. Les interfaces natives générées imposent les signatures de méthodes exactes, les types de paramètres et les types de retour définis dans la spécification. Si l'implémentation native dévie — retourner Int là où la spécification déclare Double, ou omettre un champ d'une structure — le compilateur natif rejette le build. Cela déplace les erreurs de type des crashs runtime vers les échecs de compilation.

Qu'est-ce que l'API Strict TypeScript et pourquoi est-elle importante ?

Introduite dans React Native 0.80, l'API Strict TypeScript génère les types directement depuis le code source de React Native plutôt que de maintenir des fichiers .d.ts écrits manuellement. Elle restreint les imports au package racine react-native, dépréciant les imports profonds. Cela définit une surface d'API publique stable — les refactorings internes ne peuvent pas casser le code consommateur si celui-ci n'utilise que les types stricts. L'activation se fait via "types": ["react-native/types/strict"] dans tsconfig.json.

Comment typer les paramètres de React Navigation à travers des navigateurs imbriqués ?

Il faut définir un type ParamList par navigateur et les composer en utilisant NavigatorScreenParams. Pour un navigateur à onglets imbriqué dans une pile, la liste de paramètres de la pile référence celle des onglets : type RootStack = { Main: NavigatorScreenParams<TabParamList>; Modal: { id: string } }. Chaque appel navigate() est alors vérifié à travers toute la hiérarchie d'imbrication, détectant les noms d'écran erronés ou les paramètres manquants à la compilation.

Quel problème les unions discriminées résolvent-elles dans la gestion d'état React Native ?

Les unions discriminées modélisent les états mutuellement exclusifs (chargement, erreur, chargé) comme des branches séparées d'un type union, indexées par un champ status. TypeScript réduit le type dans chaque branche d'un switch, de sorte qu'accéder à state.data n'est possible que lorsque state.status === 'loaded'. Cela empêche les états impossibles comme un indicateur de chargement affiché simultanément avec des données d'erreur — une classe de bugs que les champs optionnels et les drapeaux booléens ne peuvent pas prévenir.

Expliquer la différence entre les TurboModules et l'ancien système de Native Modules.

Les Native Modules communiquaient via le bridge asynchrone, sérialisant toutes les données en JSON. Les TurboModules utilisent JSI (JavaScript Interface) pour des appels C++ directs et synchrones — sans surcharge de sérialisation. Ils se chargent aussi de manière paresseuse (au premier usage plutôt qu'au démarrage de l'application, réduisant le temps de démarrage à froid) et utilisent Codegen pour générer des interfaces type-safe depuis les spécifications TypeScript. L'ancien système reposait sur le parsing de ReadableMap / NSDictionary avec coercition de type runtime ; les TurboModules imposent les types à la compilation.

Pour davantage de questions d'entretien React Native, le guide approfondi sur les modules natifs couvre JSI, Fabric et l'architecture bridgeless en détail.

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Conclusion

  • L'API Strict TypeScript (0.80+) restreint les imports à la surface publique stable, évitant les ruptures causées par des changements internes — à activer dans chaque nouveau projet
  • Codegen génère les interfaces natives depuis les fichiers de spécification TypeScript, déplaçant les erreurs de type des crashs runtime vers les échecs de compilation à la frontière JS-natif
  • Les paramètres de navigation typés via RootStackParamList et NativeStackScreenProps détectent les noms d'écran erronés et les paramètres manquants avant l'exécution de l'application
  • Les unions discriminées modélisent les états d'écran comme des branches mutuellement exclusives, rendant les états impossibles irreprésentables au niveau du système de types
  • Les TurboModules avec spécifications typées remplacent l'ancien parsing de ReadableMap / NSDictionary, imposant une sécurité de type complète de JavaScript à C++ jusqu'au code natif de la plateforme
  • Les hooks API génériques avec TanStack Query préservent l'inférence de type du endpoint au composant sans casts manuels

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Tags

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

Partager

Articles similaires