strict: true activa las comprobaciones del compilador que detectan parámetros de ruta indefinidos, formas de API obsoletas, y any implícito en los bridges nativos - antes de que Metro agrupe una pantalla rota. En Expo SDK 57, extiendas expo/tsconfig.base y ajustas incrementalmente en lugar de luchar contra una pared de mil errores.
// app/orders/[id].tsx - strictNullChecks + parámetros de ruta tipadosimport { useEffect, useState } from "react";import { ActivityIndicator, Text, View, StyleSheet } from "react-native";import { useLocalSearchParams } from "expo-router";import { fetchOrder, type Order } from "@/lib/api/orders";type OrderParams = { id: string;};function parseOrderParams(raw: Record<string, string | string[] | undefined>): OrderParams { const value = raw.id; const id = Array.isArray(value) ? value[0] : value; if (!id) { throw new Error("Falta parámetro de ruta: id"); } return { id };}export default function OrderDetailScreen() { const params = useLocalSearchParams(); const { id } = parseOrderParams(params); const [order, setOrder] = useState<Order | null>(null); const [error, setError] = useState<string | null>(null); useEffect(() => { let cancelled = false; fetchOrder(id) .then((data) => { if (!cancelled) setOrder(data); }) .catch((err: unknown) => { if (!cancelled) { setError(err instanceof Error ? err.message : "No se pudo cargar el pedido"); } }); return () => { cancelled = true; }; }, [id]); if (error) { return ( <View style={styles.center}> <Text style={styles.error}>{error}</Text> </View> ); } if (!order) { return ( <View style={styles.center}> <ActivityIndicator /> </View> ); } return ( <View style={styles.screen}> <Text style={styles.title}>Pedido {order.id}</Text> <Text>Estado: {order.status}</Text> <Text>Total: ${(order.totalCents / 100).toFixed(2)}</Text> </View> );}const styles = StyleSheet.create({ screen: { flex: 1, padding: 16 }, center: { flex: 1, alignItems: "center", justifyContent: "center" }, title: { fontSize: 20, fontWeight: "600" }, error: { color: "#dc2626" },});
// lib/api/orders.ts - unknown en el límite, tipado después de la validaciónimport { z } from "zod";const OrderSchema = z.object({ id: z.string(), totalCents: z.number().int().nonnegative(), status: z.enum(["pending", "shipped", "delivered"]),});export type Order = z.infer<typeof OrderSchema>;export async function fetchOrder(id: string): Promise<Order> { const res = await fetch(`https://api.example.com/orders/${id}`); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const json: unknown = await res.json(); return OrderSchema.parse(json);}
// Prefiere import type para tipos solamente - verbatimModuleSyntax refuerza la claridadimport type { Order } from "@/lib/api/orders";import { fetchOrder } from "@/lib/api/orders";// StyleProp<ViewStyle> para componentes que aceptan sobrescrituras de estiloimport type { StyleProp, ViewStyle } from "react-native";interface CardProps { title: string; style?: StyleProp<ViewStyle>;}
// Supresión temporal - prefiere @ts-expect-error con un ticket sobre @ts-ignore// @ts-expect-error RN-204 - LegacyDeviceModule sin tipo hasta que el wrapper del bridge aterriceconst reading = NativeModules.LegacyDeviceModule.getLastReading();
Desabilitar strict para poner verde CI - La compilación pasa mientras any se filtra en capas de navegación y fetch. Solución: Corrige límites primero; usa @ts-expect-error con IDs de tickets para brechas heredadas conocidas.
Ruido de noUncheckedIndexedAccess en índices seguros - Cada items[0] se convierte en T | undefined. Solución: Estreccha con if (item), usa .at(0) con una guardia, o un pequeño helper function first<T>(arr: T[]): T | undefined - evita aserciones ! genéricas.
useLocalSearchParams() sin tipo - Los enlaces profundos entregan strings; las claves faltantes se convierten en undefined en tiempo de ejecución. Solución: Analiza params en una función por pantalla; lanza o redirige cuando faltan claves requeridas.
skipLibCheck: false durante actualizaciones de SDK - Los conflictos dentro de @types/react y los tipings de RN bloquean fusiones no relacionadas con tu código. Solución: Mantén skipLibCheck: true (por defecto de Expo) a menos que mantengas forks personalizados de DefinitelyTyped.
tsc no viendo tipos de Expo Router - Los archivos generados viven bajo .expo/types. Solución: Añade .expo/types/**/*.ts a include y ejecuta npx expo customize tsconfig.json después de habilitar rutas tipadas.
Reglas de ESLint conscientes del tipo sin parserOptions.project - Reglas como @typescript-eslint/no-floating-promises silenciosamente se saltan o errorizan. Solución: Apunta ESLint a tsconfig.json o ejecuta esas reglas solo en un trabajo de CI dedicado.