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

Développer des applications React Native type-safe avec TypeScript, Codegen, TurboModules et l'API Strict TypeScript. Architecture, navigation typée, exigences de la chaîne d'outils pour 0.87 et questions d'entretien.

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 à la mi-2026. Avec la version 0.87 livrée en août, le framework impose l'API Strict TypeScript par défaut et achève une modernisation majeure de la chaîne d'outils. 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 0.87

React Native 0.87 (août 2026) impose l'API Strict TypeScript comme valeur par défaut obligatoire. Cette version relève également les exigences minimales à Node.js 22.13+, Kotlin 2.0+ et Android Gradle Plugin 9. L'option de désactivation pour les imports profonds legacy reste disponible uniquement jusqu'à la version 0.88.

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.

L'API Strict TypeScript : de l'opt-in au défaut obligatoire

L'API Strict TypeScript a été introduite dans React Native 0.80 en opt-in. Depuis la version 0.87, elle constitue le défaut obligatoire pour tous les nouveaux projets. Les types sont générés directement depuis le code source de React Native plutôt que maintenus manuellement, éliminant toute dérive entre l'API documentée et l'implémentation réelle.

tsconfig.json (configuration par défaut 0.87+)json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

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. Si une bibliothèque ou une codebase ancienne repose encore sur les imports profonds, une option de désactivation temporaire reste disponible jusqu'à la version 0.88 :

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

Cette option sera supprimée en 0.89. La migration doit intervenir avant cette échéance.

Exigences de la chaîne d'outils en 0.87

React Native 0.87 relève les versions minimales sur l'ensemble de la chaîne de build :

ComposantMinimum
Node.js22.13.0
Kotlin2.0 (fourni : 2.2.0)
Android Gradle Plugin9.x
Android compileSdk34 (min), 37 (cible)

Pour Android, AGP 9 nécessite une configuration supplémentaire :

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

Ces exigences s'appliquent à tous les projets ciblant 0.87+. Le guide de migration New Architecture couvre le contexte plus large de ces changements.

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' }) est vérifié par le compilateur
  // navigation.navigate('Profile', {}) provoque une 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é, et 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. Pour approfondir le système de modules natifs, consulter les questions d'entretien sur les modules natifs.

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 ?

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. En 0.87, elle est devenue le défaut obligatoire. 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. Une option de désactivation temporaire via customConditions reste disponible jusqu'à la version 0.88.

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.

Quelles API ont été supprimées dans React Native 0.87 ?

La version 0.87 a supprimé InteractionManager (remplacé par requestIdleCallback), les types NativeMethods / NativeMethodsMixin (remplacés par HostInstance), et plusieurs props de StatusBar. Les imports profonds vers les chemins src/private/ génèrent désormais des erreurs de type. Le flag useTurboModules a également été supprimé, les TurboModules étant désormais toujours activés.

Pour davantage de questions d'entretien React Native, le guide complet couvre l'architecture, les performances et le débogage.

Passe à la pratique !

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

Sources

Ce qu'il faut retenir de React Native TypeScript en 2026

  • L'API Strict TypeScript (obligatoire en 0.87) restreint les imports à la surface publique stable, évitant les ruptures causées par des changements internes. L'option de désactivation pour les imports profonds legacy n'est disponible que jusqu'à la version 0.88.
  • 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
  • Versions minimales en 0.87 : Node.js 22.13+, Kotlin 2.0+, AGP 9, compileSdk 34+
  • 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.

Défi du jour

Tu saurais repérer le bug en React Native ?

Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Anthony Fillion-Maillet

Écrit par

Anthony Fillion-Maillet

Fondateur de SharpSkill

Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.

Mis à jour le 25 août 2026

Tags

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

Partager

Articles similaires