React Native e TypeScript nel 2026: architettura type-safe e domande da colloquio

Crea app React Native type-safe con TypeScript, Codegen, TurboModule e la Strict TypeScript API. Pattern architetturali, navigazione tipizzata, requisiti toolchain per la 0.87 e domande da colloquio.

Architettura React Native type-safe con TypeScript, codice e dispositivi mobili

La type safety di React Native con TypeScript è maturata in modo significativo a metà 2026: la versione 0.87 rende la Strict TypeScript API lo standard obbligatorio e completa una profonda modernizzazione della toolchain. TypeScript non è più una colla opzionale tra JavaScript e codice nativo: governa l'intero contratto, dalle props dei componenti alle interfacce dei TurboModule, intercettando le incoerenze a tempo di build invece che nei log di crash in produzione.

Cosa è cambiato in 0.87

React Native 0.87 (agosto 2026) rende la Strict TypeScript API lo standard obbligatorio. Aumenta inoltre i requisiti minimi a Node.js 22.13+, Kotlin 2.0+ e Android Gradle Plugin 9. L'opt-out per i deep import legacy rimane disponibile solo fino alla 0.88.

Perché TypeScript è ormai lo standard per i progetti React Native

Ogni invocazione di npx react-native init dalla versione 0.76 genera lo scaffold di un progetto TypeScript. Il vero cambiamento, però, è avvenuto al confine nativo. Prima di Codegen, gli sviluppatori scrivevano asserzioni di tipo manuali nel passaggio da JavaScript a Objective-C o Kotlin: un contratto basato su stringhe che si rompeva silenziosamente a runtime. Codegen legge i file di specifica TypeScript e genera automaticamente le interfacce C++, Objective-C++ e Java/Kotlin. Se la specifica TypeScript dichiara un metodo che restituisce number, l'interfaccia nativa generata impone quel vincolo a tempo di compilazione.

La documentazione di React Native copre la configurazione di base, ma i pattern type-safe che contano in produzione vanno oltre: stack di navigazione tipizzati, hook API generici, union discriminate per le state machine e specifiche TurboModule guidate da Codegen.

La Strict TypeScript API: da opt-in a standard

La Strict TypeScript API è stata introdotta in React Native 0.80 come funzionalità opt-in. A partire dalla 0.87, è lo standard obbligatorio per tutti i nuovi progetti. I tipi vengono generati direttamente dal codice sorgente di React Native anziché essere mantenuti manualmente, eliminando le discrepanze tra l'API documentata e l'implementazione effettiva.

tsconfig.json (configurazione predefinita 0.87+)json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

Con questa configurazione, importare qualcosa da un sottopercorso come react-native/Libraries/Text/Text genera un errore di tipo. Tutti gli import devono provenire dal pacchetto radice react-native. Se una libreria o una codebase legacy si affida ancora ai deep import, è disponibile un opt-out temporaneo fino alla 0.88:

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

Questo opt-out verrà rimosso nella 0.89. La migrazione dovrebbe avvenire prima.

Requisiti della toolchain in 0.87

React Native 0.87 aumenta le versioni minime lungo l'intera catena di build:

ComponenteMinimo
Node.js22.13.0
Kotlin2.0 (incluso: 2.2.0)
Android Gradle Plugin9.x
Android compileSdk34 (min), 37 (target)

Per Android, AGP 9 richiede configurazione aggiuntiva:

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

Questi requisiti si applicano a tutti i progetti che puntano alla 0.87+. La guida alla migrazione verso la New Architecture copre il contesto più ampio di questi cambiamenti.

React Navigation 7.x offre un supporto TypeScript di prima classe. Il pattern chiave: definire un tipo RootStackParamList che mappa ogni nome di schermata ai parametri attesi, poi propagare quel tipo attraverso i navigatori e i componenti schermata.

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

I componenti schermata ricevono così props tipizzate senza cast manuali:

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 è string — garantito dal tipo
  // route.params.source è 'feed' | 'search' — nessun controllo a runtime
  const { articleId, source } = route.params;

  // navigation.navigate('Profile', { userId: '123' }) — verificato dai tipi
  // navigation.navigate('Profile', {}) — errore di compilazione: manca userId
  return (
    <ArticleView id={articleId} referrer={source} />
  );
}

Questo elimina un'intera categoria di errori a runtime: navigare verso una schermata con parametri errati o mancanti fallisce già a tempo di build.

Tipizzare l'hook useNavigation

Per i componenti che non sono figli diretti di una schermata, useNavigation<NativeStackNavigationProp<RootStackParamList>>() fornisce la stessa type safety senza prop drilling.

Costruire un TurboModule type-safe con Codegen

I TurboModule sostituiscono il vecchio sistema dei Native Modules. Il file di specifica TypeScript funge da unica fonte di verità: Codegen genera da esso le interfacce native. Se specifica e implementazione nativa divergono, la build fallisce.

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

Eseguire npx react-native codegen genera le corrispondenti interfacce C++, Objective-C++ e Java. L'implementazione nativa deve rispettare esattamente ogni firma di metodo. Ad esempio, getStorageInfo deve restituire un oggetto con tre campi numerici: restituire una forma diversa causa un errore di compilazione sul lato nativo.

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

    // Tipo di ritorno imposto da NativeDeviceInfoSpec generato
    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)
    }
}

Questo approccio elimina il parsing di ReadableMap e NSDictionary che causava bug silenziosi di coercizione dei tipi nella vecchia architettura. Per un approfondimento sul sistema dei native module, consultare le domande da colloquio sui native modules.

Pronto a superare i tuoi colloqui su React Native?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Data fetching generico con hook API tipizzati

Un pattern di hook tipizzato e riutilizzabile evita di duplicare la logica di fetch tra le schermate preservando al contempo l'inferenza completa dei tipi:

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

// Utilizzo — T viene inferito come Article[]
interface Article {
  id: string;
  title: string;
  publishedAt: string;
}

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

Il parametro generico T attraversa l'intera catena: dal punto di chiamata dell'hook, attraverso la funzione di query, fino al componente che consuma il risultato. Nessun cast as, nessun tipo any.

Union discriminate per le state machine

Gli stati complessi di una schermata, caricamento, errore, vuoto, caricato, si modellano al meglio come union discriminate, anziché come un insieme di campi opzionali:

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':
      // qui state.error è string — TypeScript restringe automaticamente
      return <ErrorBanner message={state.error} retries={state.retryCount} />;
    case 'empty':
      return <EmptyState message={state.message} />;
    case 'loaded':
      // state.data è T — completamente tipizzato
      return renderItem(state.data);
  }
}

Questo pattern rende gli stati impossibili non rappresentabili. Uno stato loading non può portare accidentalmente data obsoleti e uno stato error include sempre il contesto per il debug.

Evitare gli oggetti di stato parziali

Un anti-pattern comune: { isLoading: boolean; error?: string; data?: T }. Consente stati come { isLoading: true, error: 'fail', data: [...] }, tre segnali contraddittori contemporaneamente. Le union discriminate lo impediscono a livello di tipo.

Domande da colloquio su React Native e TypeScript

Queste domande riflettono ciò che i team di mobile engineering senior chiedono nei colloqui del 2026, ora che la New Architecture e TypeScript sono lo standard.

Come fa Codegen a imporre la type safety attraverso il confine JavaScript-nativo?

Codegen legge i file di specifica TypeScript (o Flow) e genera codice di interfaccia C++, Objective-C++ e Java/Kotlin. Le interfacce native generate impongono esattamente le firme dei metodi, i tipi dei parametri e i tipi di ritorno definiti nella specifica. Se l'implementazione nativa devia, restituendo Int dove la specifica dichiara Double, oppure omettendo un campo da una struct, il compilatore nativo rifiuta la build. Questo sposta gli errori di tipo dai crash a runtime ai fallimenti a tempo di build.

Cos'è la Strict TypeScript API e perché è importante?

La Strict TypeScript API genera i tipi direttamente dal codice sorgente di React Native invece di mantenere file .d.ts scritti a mano. Nella 0.87, è diventata lo standard obbligatorio. Limita gli import al pacchetto radice react-native, deprecando i deep import. Questo definisce una superficie pubblica dell'API stabile: i refactoring interni non possono rompere il codice consumer finché questo usa solo i tipi strict. Un opt-out temporaneo tramite customConditions rimane disponibile fino alla 0.88.

Come si tipizzano i parametri di React Navigation tra navigatori annidati?

Definire un tipo ParamList per ogni navigatore e comporli con NavigatorScreenParams. Per un tab navigator annidato dentro uno stack, la param list dello stack referenzia quella del tab: type RootStack = { Main: NavigatorScreenParams<TabParamList>; Modal: { id: string } }. Ogni chiamata navigate() viene così verificata dai tipi attraverso l'intera gerarchia di annidamento, intercettando nomi di schermata errati o parametri mancanti a tempo di compilazione.

Quale problema risolvono le union discriminate nello state management di React Native?

Le union discriminate modellano stati mutuamente esclusivi (caricamento, errore, caricato) come rami separati di un tipo union, distinti da un campo status. TypeScript restringe il tipo in ogni ramo di uno switch, così l'accesso a state.data è possibile solo quando state.status === 'loaded'. Questo evita stati impossibili come un indicatore di caricamento mostrato insieme a dati di errore, una categoria di bug che i campi opzionali e i flag booleani non riescono a prevenire.

Spiega la differenza tra i TurboModule e il vecchio sistema dei Native Modules.

I Native Modules comunicavano tramite la bridge asincrona, serializzando tutti i dati in JSON. I TurboModule usano JSI (JavaScript Interface) per chiamate C++ sincrone e dirette, senza overhead di serializzazione. Inoltre si caricano in modo lazy (al primo utilizzo invece che all'avvio dell'app, riducendo il tempo di cold start) e usano Codegen per generare interfacce type-safe dalle specifiche TypeScript. Il vecchio sistema si affidava al parsing di ReadableMap / NSDictionary con coercizione dei tipi a runtime; i TurboModule impongono i tipi a tempo di compilazione.

Quali API sono state rimosse in React Native 0.87?

La 0.87 ha rimosso InteractionManager (usare requestIdleCallback), i tipi NativeMethods / NativeMethodsMixin (usare HostInstance) e diverse props di StatusBar. I deep import ai percorsi src/private/ sono ora errori di tipo. È stato rimosso anche il flag useTurboModules poiché i TurboModule sono ora sempre abilitati.

Per altre domande da colloquio su React Native, la guida completa copre architettura, performance e temi di debugging.

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Fonti

Da ricordare su React Native TypeScript nel 2026

  • La Strict TypeScript API (obbligatoria nella 0.87) limita gli import alla superficie pubblica stabile, evitando rotture dovute a modifiche interne. L'opt-out per i deep import legacy è disponibile solo fino alla 0.88
  • Codegen genera le interfacce native dai file di specifica TypeScript, spostando gli errori di tipo dai crash a runtime ai fallimenti a tempo di build attraverso il confine JS-nativo
  • Requisiti minimi toolchain nella 0.87: Node.js 22.13+, Kotlin 2.0+, AGP 9, compileSdk 34+
  • I parametri di navigazione tipizzati via RootStackParamList e NativeStackScreenProps intercettano nomi di schermata errati e parametri mancanti prima che l'app venga eseguita
  • Le union discriminate modellano gli stati delle schermate come rami mutuamente esclusivi, rendendo gli stati impossibili non rappresentabili a livello di tipo
  • I TurboModule con specifiche tipizzate sostituiscono il vecchio parsing di ReadableMap / NSDictionary, imponendo type safety completa da JavaScript fino al codice nativo della piattaforma passando per il C++
  • Gli hook API generici con TanStack Query preservano l'inferenza dei tipi dall'endpoint al componente senza cast manuali

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Sfida del giorno

Sapresti trovare il bug in React Native?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 25 agosto 2026

Tag

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

Condividi

Articoli correlati