React Native e TypeScript em 2026: Arquitetura Type-Safe e Perguntas de Entrevista

Desenvolver aplicações React Native type-safe com TypeScript, Codegen, TurboModules e a API Strict TypeScript. Arquitetura, navegação tipada, requisitos de cadeia de ferramentas para 0.87 e perguntas de entrevista.

Arquitetura type-safe React Native TypeScript com código e dispositivos móveis

A segurança de tipos no React Native atingiu um patamar significativo em meados de 2026. Com a versão 0.87 lançada em agosto, o framework estabelece a API Strict TypeScript como padrão obrigatório e completa uma modernização importante da cadeia de ferramentas. O TypeScript deixou de ser uma camada opcional entre JavaScript e código nativo: agora ele governa todo o contrato, desde as props de componentes até as interfaces de TurboModules, capturando incompatibilidades em tempo de compilação em vez de nos logs de crash em produção.

O que mudou em 0.87

React Native 0.87 (agosto 2026) estabelece a API Strict TypeScript como padrão obrigatório. Também eleva os requisitos mínimos para Node.js 22.13+, Kotlin 2.0+ e Android Gradle Plugin 9. A opção de desativação para imports profundos legacy permanece disponível apenas até a versão 0.88.

Por que o TypeScript se tornou o padrão para projetos React Native

Cada chamada de npx react-native init gera um projeto TypeScript desde a versão 0.76. Mas a mudança real aconteceu na fronteira nativa. Antes do Codegen, os desenvolvedores escreviam asserções de tipo manuais ao cruzar de JavaScript para Objective-C ou Kotlin, um contrato baseado em strings que falhava silenciosamente em tempo de execução. O Codegen lê os arquivos de especificação TypeScript e gera automaticamente as interfaces em C++, Objective-C++ e Java/Kotlin. Se a especificação TypeScript declara um método que retorna number, a interface nativa gerada impõe essa restrição em tempo de compilação.

A documentação do React Native cobre a configuração básica, mas os padrões type-safe que realmente importam em produção vão além: pilhas de navegação tipadas, hooks de API genéricos, uniões discriminadas para máquinas de estado e especificações de TurboModules geradas pelo Codegen.

A API Strict TypeScript: de opt-in para padrão obrigatório

A API Strict TypeScript foi introduzida no React Native 0.80 como recurso opt-in. A partir de 0.87, ela é o padrão obrigatório para todos os novos projetos. Os tipos são gerados diretamente a partir do código-fonte do React Native em vez de serem mantidos manualmente, eliminando divergências entre a API documentada e a implementação real.

tsconfig.json (configuração padrão 0.87+)json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

Com essa configuração, importar qualquer coisa de um subcaminho como react-native/Libraries/Text/Text gera um erro de tipo. Todas as importações devem vir do pacote raiz react-native. Se uma biblioteca ou codebase antiga ainda depende de imports profundos, uma opção de desativação temporária está disponível até 0.88:

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

Essa opção será removida em 0.89. A migração deve acontecer antes dessa data.

Requisitos da cadeia de ferramentas em 0.87

React Native 0.87 eleva as versões mínimas em toda a cadeia de build:

ComponenteMínimo
Node.js22.13.0
Kotlin2.0 (incluído: 2.2.0)
Android Gradle Plugin9.x
Android compileSdk34 (mín), 37 (alvo)

Para Android, AGP 9 requer configuração adicional:

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

Esses requisitos se aplicam a todos os projetos que visam 0.87+. O guia de migração New Architecture cobre o contexto mais amplo dessas mudanças.

O React Navigation 7.x oferece suporte TypeScript de primeira classe. O padrão fundamental: definir um tipo RootStackParamList que mapeia cada nome de tela para seus parâmetros esperados e, em seguida, propagar esse tipo através dos navegadores e componentes de tela.

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

Os componentes de tela recebem props tipadas sem conversão manual:

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, garantido pelo tipo
  // route.params.source é 'feed' | 'search', sem verificação em runtime
  const { articleId, source } = route.params;

  // navigation.navigate('Profile', { userId: '123' }) é verificado pelo compilador
  // navigation.navigate('Profile', {}) causa erro de compilação: userId ausente
  return (
    <ArticleView id={articleId} referrer={source} />
  );
}

Esse mecanismo elimina uma classe inteira de erros em tempo de execução: navegar para uma tela com parâmetros incorretos ou ausentes falha em tempo de compilação.

Tipagem do hook useNavigation

Para componentes que não são filhos diretos de uma tela, utilizar useNavigation<NativeStackNavigationProp<RootStackParamList>>() fornece a mesma segurança de tipos sem prop drilling.

Construindo um TurboModule Type-Safe com Codegen

Os TurboModules substituem o antigo sistema de Native Modules. O arquivo de especificação TypeScript serve como a fonte única de verdade, e o Codegen gera as interfaces nativas a partir dele. Se a especificação e a implementação nativa divergem, o build falha.

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

A execução de npx react-native codegen gera as interfaces correspondentes em C++, Objective-C++ e Java. A implementação nativa deve corresponder exatamente a cada assinatura de método. Por exemplo, getStorageInfo deve retornar um objeto com três campos numéricos; retornar uma estrutura diferente causa um erro de compilação no lado nativo.

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

    // O tipo de retorno é imposto pelo NativeDeviceInfoSpec gerado
    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)
    }
}

Essa abordagem elimina o parsing de ReadableMap e NSDictionary que causava bugs silenciosos de coerção de tipos na arquitetura anterior. Para aprofundar o sistema de módulos nativos, consultar as perguntas de entrevista sobre módulos nativos.

Pronto para mandar bem nas entrevistas de React Native?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Hooks de API Genéricos com Tipagem Completa

Um padrão de hook tipado reutilizável evita a duplicação da lógica de fetch entre telas, preservando a inferência de tipos completa:

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

// Uso, T é inferido como Article[]
interface Article {
  id: string;
  title: string;
  publishedAt: string;
}

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

O parâmetro genérico T se propaga por toda a cadeia: do ponto de chamada do hook, passando pela função de consulta, até o componente que consome o resultado. Sem casts as, sem tipos any.

Uniões Discriminadas para Máquinas de Estado

Estados de tela complexos (carregamento, erro, vazio, carregado) são melhor modelados como uniões discriminadas em vez de um conjunto de campos opcionais:

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 é string aqui, TypeScript restringe o tipo automaticamente
      return <ErrorBanner message={state.error} retries={state.retryCount} />;
    case 'empty':
      return <EmptyState message={state.message} />;
    case 'loaded':
      // state.data é T, completamente tipado
      return renderItem(state.data);
  }
}

Esse padrão torna os estados impossíveis irrepresentáveis. Um estado loading não pode carregar acidentalmente dados obsoletos, e um estado error sempre inclui contexto para depuração.

Evitar objetos de estado parciais

Um anti-padrão comum: { isLoading: boolean; error?: string; data?: T }. Esse esquema permite estados como { isLoading: true, error: 'fail', data: [...] }, três sinais contraditórios ao mesmo tempo. As uniões discriminadas previnem isso no nível do sistema de tipos.

Perguntas de Entrevista React Native TypeScript

Estas perguntas refletem o que as equipes de engenharia móvel senior perguntam em entrevistas em 2026, agora que a New Architecture e o TypeScript se tornaram padrão.

Como o Codegen garante a segurança de tipos na fronteira JavaScript-nativo?

O Codegen lê os arquivos de especificação TypeScript (ou Flow) e gera código de interface em C++, Objective-C++ e Java/Kotlin. As interfaces nativas geradas impõem as assinaturas de métodos exatas, os tipos de parâmetros e os tipos de retorno definidos na especificação. Se a implementação nativa divergir (retornando Int onde a especificação declara Double, ou omitindo um campo de uma struct), o compilador nativo rejeita o build. Isso move os erros de tipo dos crashes em runtime para falhas em tempo de compilação.

O que é a API Strict TypeScript e por que ela é importante?

A API Strict TypeScript gera os tipos diretamente a partir do código-fonte do React Native, em vez de manter arquivos .d.ts escritos manualmente. Em 0.87, ela se tornou o padrão obrigatório. Ela restringe as importações ao pacote raiz react-native, depreciando importações profundas. Isso define uma superfície de API pública estável: refatorações internas não podem quebrar o código do consumidor se este utilizar apenas tipos estrictos. Uma opção de desativação temporária via customConditions permanece disponível até 0.88.

Como tipar os parâmetros do React Navigation através de navegadores aninhados?

Define-se um tipo ParamList por navegador e compõem-se usando NavigatorScreenParams. Para um navegador de abas aninhado dentro de uma pilha, a lista de parâmetros da pilha referencia a das abas: type RootStack = { Main: NavigatorScreenParams<TabParamList>; Modal: { id: string } }. Cada chamada navigate() é verificada através de toda a hierarquia de aninhamento, capturando nomes de tela errados ou parâmetros ausentes em tempo de compilação.

Qual problema as uniões discriminadas resolvem no gerenciamento de estado do React Native?

As uniões discriminadas modelam estados mutuamente exclusivos (carregamento, erro, carregado) como branches separados de um tipo union, indexados por um campo status. O TypeScript restringe o tipo em cada branch de um switch, de modo que acessar state.data só é possível quando state.status === 'loaded'. Isso previne estados impossíveis como um indicador de carregamento sendo exibido junto com dados de erro, uma classe de bugs que campos opcionais e flags booleanos não conseguem prevenir.

Explicar a diferença entre TurboModules e o antigo sistema de Native Modules.

Os Native Modules se comunicavam através da bridge assíncrona, serializando todos os dados em JSON. Os TurboModules utilizam JSI (JavaScript Interface) para chamadas C++ diretas e síncronas, sem overhead de serialização. Eles também carregam de forma lazy (no primeiro uso em vez da inicialização do app, reduzindo o tempo de cold start) e utilizam o Codegen para gerar interfaces type-safe a partir das especificações TypeScript. O sistema anterior dependia do parsing de ReadableMap / NSDictionary com coerção de tipos em runtime; os TurboModules impõem os tipos em tempo de compilação.

Quais APIs foram removidas no React Native 0.87?

A versão 0.87 removeu InteractionManager (usar requestIdleCallback), os tipos NativeMethods / NativeMethodsMixin (usar HostInstance), e várias props do StatusBar. Importações profundas para caminhos src/private/ agora geram erros de tipo. A flag useTurboModules também foi removida, já que os TurboModules agora estão sempre habilitados.

Para mais perguntas de entrevista React Native, o guia completo cobre arquitetura, performance e tópicos de depuração.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Fontes

O que lembrar sobre React Native TypeScript em 2026

  • A API Strict TypeScript (obrigatória em 0.87) restringe as importações à superfície pública estável, prevenindo quebras causadas por mudanças internas. A opção de desativação para imports profundos legacy está disponível apenas até 0.88.
  • O Codegen gera as interfaces nativas a partir dos arquivos de especificação TypeScript, movendo os erros de tipo dos crashes em runtime para falhas de compilação na fronteira JS-nativo
  • Versões mínimas em 0.87: Node.js 22.13+, Kotlin 2.0+, AGP 9, compileSdk 34+
  • Os parâmetros de navegação tipados via RootStackParamList e NativeStackScreenProps capturam nomes de tela errados e parâmetros ausentes antes da execução da aplicação
  • As uniões discriminadas modelam os estados de tela como branches mutuamente exclusivos, tornando os estados impossíveis irrepresentáveis no nível do sistema de tipos
  • Os TurboModules com especificações tipadas substituem o antigo parsing de ReadableMap / NSDictionary, impondo segurança de tipos completa de JavaScript através de C++ até o código nativo da plataforma
  • Os hooks de API genéricos com TanStack Query preservam a inferência de tipos do endpoint ao componente sem casts manuais

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Desafio do dia

Você saberia encontrar o bug em React Native?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 25 de agosto de 2026

Tags

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

Compartilhar

Artigos relacionados