Inicio / Artículos / Errores comunes de TypeScript en React y cómo solucionarlos

Errores comunes de TypeScript en React y cómo solucionarlos

Una guía práctica sobre cinco categorías recurrentes de errores en TypeScript en aplicaciones React: props, eventos, estado, datos asíncronos e hijos, con soluciones claras para cada una.

3774 palabras

Si vienes de JavaScript, TypeScript puede parecer intimidante al principio. Durante los primeros días, podría dar la impresión de que el compilador está trabajando en tu contra: líneas onduladas rojas y amarillas por todas partes, mensajes enigmáticos que parecen no llevar a ninguna parte... y en algún momento podrías empezar a cuestionarte si cambiar a TypeScript fue realmente una buena idea, preguntándote si volver al JavaScript puro no sería más sencillo.

Pero he aquí el detalle: incorporar TypeScript a un códigobase de React es una de las decisiones más inteligentes que puedes tomar si te importa mantener un código de calidad a largo plazo.

Y esos errores y advertencias con los que te encuentras constantemente? Definitivamente vale la pena superarlos; solo tienes que aprender a reconocer los patrones recurrentes detrás de ellos.

La mayoría de los problemas con TypeScript que se presentan al desarrollar aplicaciones React no son en absoluto aleatorios. Los mismos pocos errores aparecen una y otra vez en diferentes proyectos, y una vez que los hayas resuelto unas cuantas veces, comenzarás a detectarlos al instante y sabrás exactamente qué significan. Este artículo analiza cinco categorías de errores que se presentan en prácticamente todos los proyectos React basados en TypeScript, explicando por qué ocurre cada uno y cómo resolverlo.

Un consejo antes de comenzar: siempre lee todo el mensaje de error. Los errores de TypeScript pueden parecer intimidantes a primera vista, pero en realidad siguen una estructura bastante predecible. No dejes que la gran cantidad de texto te desoriente; si alguna vez no estás seguro por dónde empezar, la última línea del mensaje suele ser la parte más útil en la que concentrarte.

Dicho esto, empecemos.

1. Errores en las propiedades de los componentes

Los props son la base de cada componente React, por lo que tiene sentido que los errores de tipo relacionados con ellos sean generalmente los primeros con los que se encuentran los desarrolladores.

El error: La propiedad 'X' no existe en el tipo '{}'.

Esto ocurre cuando utilizas un prop en tu componente sin haberle definido nunca un tipo.

// This won't give any errors in JavaScript...
function UserCard({ name, email }) {
  return (
    <div>
      <h3>{name}</h3>
      <p>{email}</p>
    </div>
  );
};

// But in TypeScript...
// Error: Parameter 'name' implicitly has an 'any' type
// Error: Parameter 'email' implicitly has an 'any' type

Este error aparece simplemente porque a los props nunca se les asignaron tipos explícitos.

La solución: declara una interfaz que describa tus props.

interface UserCardProps {
  name: string;
  email: string;
};

function UserCard({ name, email}: UserCardProps) {
  return (
    <div>
      <h3>{name}</h3>
      <p>{email}</p>
    </div>
  );
};

El error: El tipo 'string' no se puede asignar al tipo 'number'.

Este es bastante sencillo: significa que pasaste un prop con un valor del tipo incorrecto.

La solución: verifica el tipo del valor que estás pasando al prop.

interface ItemForSaleProps {
  imgUrl: string;
  itemName: string;
  amount: number;
  currency: string;
};

function ItemForSale({
  itemName,
  imgUrl,
  amount,
  currency
}: ItemForSaleProps) {
  return (
    <div class="item-for-sale">
      <img src={imgUrl} alt="item image" />
      <h5>{itemName}</h5>
      <p>{`${currency} ${amount.toFixed(2)}`}</p>
    </div>
  );
};

// Error happens when passing a string where a number is expected.
<ItemForSale
  imgUrl="api.example.com/image"
  itemName="Sample"
  amount="10.99"
  currency="USD"
/>

// Should be like this
<ItemForSale
  imgUrl="api.example.com/image"
  itemName="Sample"
  amount={10.99}
  currency="USD"
/>

¿Observa la diferencia entre las dos versiones? El componente ItemForSale espera que amount sea un number; por lo tanto, pasar una cadena genera un error, mientras que pasar un valor numérico real es correcto. Esto demuestra cómo TypeScript hace exactamente lo para lo que fue diseñado: detectar un posible error antes de que su código se ejecute. En este ejemplo, llamar a .toFixed(2) sobre la cadena '10.99' causaría una falla en tiempo de ejecución, pero TypeScript señala el problema durante la compilación.

El error: La propiedad 'X' falta en el tipo '{}' pero es necesaria en el tipo 'Props'.

Esto ocurre cuando una propiedad se marca como obligatoria en su interfaz, pero uno olvida pasarla realmente al utilizar el componente.

interface CustomButtonProps {
  label: string;
  onClick: () => void;
};

function CustomButton({ label, onClick }: CustomButtonProps) {
  return <button onClick={onClick}>{label}</button>;
};

// Bad usage:
<CustomButton label="Submit" />
// Error: Property 'onClick' is missing in type '{ label: string; }'
// but required in type 'ButtonProps'

La solución: verifica nuevamente tanto las definiciones de las propiedades en tu componente como las propiedades que realmente estás proporcionando al renderizarlo.

// Correct usage:
<CustomButton label="Submit" onClick={handleSubmit} />

A veces tendrás propiedades que no siempre son necesarias. En esos casos, puedes marcar una propiedad como opcional con un ?. Recuerda que cada vez que hagas una propiedad opcional, también debes proporcionar un valor por defecto o algún tipo de verificación de null para manejar su ausencia de forma segura.

interface CustomButtonProps {
  label: string;
  onClick: () => void;
  disabled?: boolean;   // this prop is now optional
  className?: string;   // this one too
};

function CustomButton({ label, onClick, disabled = false, className }: CustomButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={className}
    >
      {label}
    </button>
  );
};

// This component now works with or without the optional props
<CustomButton label="Submit" onClick={handleSubmit} />
<CustomButton label="Submit" onClick={handleSubmit} disabled={true} />

Ahora que existen las propiedades opcionales, hay otro error que merece mención.

El error: El tipo 'X | undefined' no se puede asignar al tipo 'X'.

Hacer que una propiedad sea opcional agrega automáticamente undefined a su tipo, lo que causa problemas si intentas usar ese valor sin verificar primero si existe.

interface ProfileProps {
  name: string;
  bio?: string;
};

function Profile({ name, bio }: ProfileProps) {
  return (
    <p>{name}</p>
    <p>{bio.toUpperCase()}</p>
    // Error: Object is possibly 'undefined'
  );
};

En general, existen tres formas de abordar esto:

Opción 1: proporcionar un valor por defecto durante el desestructurado de props.

// bio is always a string, will render a default text when there's
// no bio provided
function Profile({ name, bio = 'No bio available' }: ProfileProps) {
  return (
    <p>{name}</p>
    <p>{bio.toUpperCase()}</p>
  );
};

Opción 2: recurrir a la cadena opcional.

// returns undefined if bio is undefined
function Profile({ name, bio }: ProfileProps) {
  return (
    <p>{name}</p>
    <p>{bio?.toUpperCase()}</p>
  );
};

Opción 3: utilizar renderizado condicional.

// will only render if bio exists
function Profile({ name, bio }: ProfileProps) {
  return (
    <p>{name}</p>
    <p>{bio && bio.toUpperCase()}</p>
  );
};

2. Errores en los manejadores de eventos

Cualquier componente de React que responda a la interacción del usuario necesita manejadores de eventos, y TypeScript incluye tipos muy precisos para cada tipo de evento DOM. Error en la definición de estos tipos es una de las fuentes más frecuentes de confusión para los desarrolladores que trabajan con código de React tipado.

El problema: el parámetro 'e' tiene implícitamente un tipo 'any'.

Esto ocurre cuando escribes un manejador de eventos como una función independiente fuera de tu JSX y olvidas anotar el parámetro del evento.

// Error: Parameter 'e' implicitly has an 'any' type
const handleClick = (e) => {
  e.preventDefault();
};

La solución: Asocie el tipo de evento de React correspondiente al parámetro.

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  e.preventDefault();
};

Cabe señalar que cuando escribe el manejador directamente dentro de su JSX, TypeScript puede determinar el tipo por sí mismo, por lo que este error en particular no aparecerá en ese caso.

<button onClick={(e) => {
  e.preventDefault() // e is automatically React.MouseEvent<HTMLButtonElement>
}}>
  Click here
</button>

El problema: La propiedad 'value' no existe en el tipo 'EventTarget'.

Este podría ser el error de TypeScript más buscado entre los desarrolladores de React; también es uno de los más difíciles de entender la primera vez que se presenta. Aparece cuando intenta leer e.target.value dentro de un manejador de cambios.

// This will give an error because TS doesn't know target is an input element.
const handleChange = (e: React.ChangeEvent) => {
  console.log(e.target.value);
  // Error: Property 'value' does not exist on type 'EventTarget'
};

La causa raíz es que EventTarget es una interfaz genérica del DOM, por lo que TypeScript no tiene forma de saber que, en este handler específico, el objetivo es en realidad un elemento <input> que casualmente expone una propiedad value.

La solución: Proporcionar el tipo de elemento específico como parámetro genérico para el tipo de evento.

// TypeScript now knows the target is an HTMLInputElement
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  console.log(e.target.value);
};

El mismo patrón funciona para otros elementos: en el caso de un elemento select, sustituir HTMLInputElement por HTMLSelectElement, por ejemplo. Aquí hay una referencia rápida para algunos de los tipos de evento que se usarán con mayor frecuencia:

// Click events
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
onClick: (e: React.MouseEvent<HTMLDivElement>) => void

// Input change events
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void

// Form submission
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void

// Keyboard events
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void
onKeyUp: (e: React.KeyboardEvent<HTMLInputElement>) => void

// Focus events
onFocus: (e: React.FocusEvent<HTMLInputElement>) => void
onBlur: (e: React.FocusEvent<HTMLInputElement>) => void

Dado que estamos hablando de manejadores de eventos, es posible que se pregunte cómo escribirlos correctamente al pasarlos como propiedades a los componentes hijos. En ese caso, deben definirse como funciones que acepten el objeto de evento correspondiente.

interface SearchInputProps {
  onSearch: (e: React.ChangeEvent<HTMLInputElement>) => void;
  onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
};

function SearchInput({ onSearch, onSubmit }: SearchInputProps) {
  return (
    <form onSubmit={onSubmit}>
      <input type="text" onChange={onSearch} />
      <button type="submit">Search</button>
    </form>
  );
};

Y si un manejador en particular no necesita recibir el evento en absoluto, puede simplificarse su tipo en consecuencia.

interface SearchInputProps {
  onClick: () => void;
  onHover: () => void;
};

3. useState y useRef

Los hooks están en el centro del desarrollo moderno con React, por lo que no es sorprendente que useState y useRef tengan sus propias particularidades en TypeScript.

El problema: El argumento de tipo 'X' no se puede asignar al parámetro de tipo 'never'.

Este es uno de los errores más desconcertantes con los que puedes encontrarte. Ocurre cuando inicializas useState con un array vacío, lo que hace que TypeScript infiera el tipo del estado como never[]. Así es como se ve:

// TypeScript infers state type as never[]
const [items, setItems] = useState([]);

// So later when we try to set a value...
setItems([{ id: 1, name: 'Item 1' }]);
// Error: Argument of type '{ id: number; name: string; }[]' is not assignable
// to parameter of type 'never[]'

La razón detrás de esto: cuando el valor inicial de tu estado es un array vacío, TypeScript no tiene forma de adivinar qué tipo de elementos habrá dentro de él, por lo que utiliza por defecto el tipo never[].

La solución: Siempre proporciona un argumento de tipo explícito cuando tu estado inicial sea un array vacío o null.

interface Item {
  id: number;
  name: string;
}

// ✅ Explicitly typed
const [items, setItems] = useState<Item[]>([]);
const [selectedItem, setSelectedItem] = useState<Item | null>(null);

En ese fragmento, el estado selectedItem está tipado explícitamente para aceptar ya sea un objeto Item o null. Cada vez que esperes que un estado comience vacío y se llene posteriormente, debes especificarlo claramente para TypeScript; de lo contrario, encontrarás este error:

El tipo 'null' no se puede asignar al tipo 'X'.

interface User {
  id: number;
  name: string;
  email: string;
}

// TypeScript infers state as User, not User | null
const [user, setUser] = useState<User>({} as User); // Dangerous cast

// Later...
if (user.name) { /* ... */ }
// This might not catch the case where user is empty

La solución: Utiliza un tipo union que incluya null para que TypeScript entienda que los datos aún no se han cargado.

// Correctly typed, can be type User or null
const [user, setUser] = useState<User | null>(null);

// Now TypeScript forces you to handle the null case
if (user) {
  console.log(user.name); // TypeScript knows user is User here
}

// Or with optional chaining:
console.log(user?.name); // string | undefined

La misma idea se aplica cuando tu estado contiene un objeto más complejo. Supongamos que estás modelando el estado de un formulario de contacto con varios campos: el enfoque correcto es definir primero una interfaz que describa esa estructura.

interface FormState {
  name: string;
  email: string;
  message: string;
  isSubmitting: boolean;
}

function SubmitMessageForm() {
  const [form, setForm] = useState<FormState>({
    name: '',
    email: '',
    message: '',
    isSubmitting: false,
  });

  // Then do partial updates with spread operator
  const handleChange = (field: keyof FormState, value: string) => {
    setForm(prevState => ({ ...prevState, [field]: value }));
  };
}

El problema: El objeto podría ser 'null'.

Esto ocurre constantemente cuando un reference creado con useRef comienza como null y está destinado a apuntar a un elemento DOM. TypeScript marcará con una advertencia cualquier intento de usar ese reference antes de confirmar que realmente contiene algún valor.

const inputRef = useRef<HTMLInputElement>(null);

// Typescript here knows that inputRef.current might be null
inputRef.current.focus();

// Error: Object is possibly 'null'

La solución: verificar que current esté asignado antes de usarlo. Una vez que TypeScript ve esa verificación, deja de emitir advertencias porque ya no puede demostrar que el valor pueda ser null.

// Null check before use
const handleFocus = () => {
  if (inputRef.current) {
    inputRef.current.focus();
  }
};

// Or if you're more into one-liners, you can use optional chaining
inputRef.current?.focus();

Antes de pasar a la siguiente categoría, hay un cambio importante que merece ser mencionado. React 19 modificó el comportamiento de useRef de una manera que confunde a muchos desarrolladores: específicamente, la distinción entre RefObject<T|null> y MutableRefObject<T>. Lo que determina ahora el comportamiento no es el valor inicial que se pasa, sino el parámetro de tipo genérico que se proporciona al hook.

Antes de React 19, llamar a useRef(null) siempre devolvía un MutableRefObject<T>, por lo que current siempre podía ser reasignado.

// This worked fine
const ref = useRef<HTMLInputElement | null>(null);
ref.current = someElement;

A partir de React 19, el tipo genérico que se especifique determina si current es editable.

const ref = useRef<HTMLInputElement>(null);
ref.current = someElement;
// The above will result in an error.

Aquí, como el parámetro genérico es un tipo de elemento HTML, React 19 trata al ref como uno que apunta al DOM, por lo que se vuelve de solo lectura y es gestionado efectivamente por React mismo.

Si en cambio necesitas un ref para almacenar un valor mutable en lugar de un nodo DOM, haz lo siguiente:

// Let's suppose we're building a timer
const ref = useRef<ReturnType<typeof setTimeout> | null>(null);
ref.current = setTimeout(() => {}, 1000);

Esto funciona porque el tipo pasado al genérico no es un tipo de elemento HTML, así que React lo trata como un ref mutable normal; current sigue siendo asignable.

La regla para React 19: cuando el tipo genérico es un tipo de elemento HTML, current se vuelve de solo lectura y es controlado por React. Cuando es cualquier otro tipo, current sigue siendo escribible. En resumen:

  • useRef<HTMLElement>(null) para nodos DOM gestionados por React (de solo lectura)
  • useRef<T|null>(null) para cualquier otro valor mutable que desee almacenar usted mismo
  • 4. Datos asíncronos y errores de API

    Una vez que comienza a obtener datos desde una API, aparece un conjunto nuevo de errores de TypeScript, ya que los datos obtenidos inician como unknown y deben pasar por varias transformaciones antes de convertirse en algo que pueda utilizarse con seguridad.

    El error: El tipo 'unknown' no se puede asignar al tipo 'X'.

    Por defecto, el resultado de una llamada a fetch se tipifica como unknown, ya que TypeScript no tiene conocimiento integrado sobre la forma en que devuelve un endpoint determinado.

    // Fetch response is unknown
    const response = await fetch('/api/users');
    const data = await response.json(); // data: any (in older TS) or unknown
    
    // So when trying to use the fetched data:
    const userName = data.name;
    
    // With the code above, you might get a warning, something like
    // 'data' is of type 'unknown'
    

    La solución: declare un tipo explícito para la respuesta de su API.

    interface User {
      id: number;
      name: string;
      email: string;
    };
    

    A partir de ahí, generalmente tiene dos opciones para aplicar ese tipo:

    // Option 1: Type assertion. Only use this when you trust the API shape.
    const response = await fetch('/api/users/1');
    const data = await response.json() as User;
    console.log(data.name); // You'll see that you have a string here
    
    //////////////////////////////////////////////////////////////////////////
    
    // Option 2: Create a generic fetch wrapper, this is a safer approach
    // and it's reusable.
    async function fetchJSON<T>(url: string): Promise<T> {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error: ${response.status}`);
      }
      return response.json() as Promise<T>;
    }
    const user = await fetchJSON<User>('/api/users/1');
    console.log(user.name); // TypeScript now knows this is a string
    

    5. Hijos y errores de JSX

    Los componentes en React suelen aceptar y renderizar children, y TypeScript ofrece varios tipos que se superponen para describir qué pueden ser esos hijos. Conocer las diferencias entre ellos te ayuda a evitar toda una serie de errores de tipo.

    Tres tipos suelen usarse como si fueran intercambiables, aunque en realidad no lo son:

    • ReactNode
    • ReactElement
    • JSX.Element

    De estos, ReactNode y ReactElement son realmente distintos, mientras que JSX.Element es en realidad solo otro nombre para ReactElement.

    import { ReactNode, ReactElement } from 'react';
    
    // ReactNode is the most permissive one, accepts basically
    // everything React can render:
    // string, number, boolean, null, undefined, ReactElement, arrays...
    
    type ReactNode =
      | ReactElement
      | string
      | number
      | boolean
      | null
      | undefined
      | ReactPortal
      | Iterable<ReactNode>;
    
    // ReactElement is literally a React element created by
    // JSX or React.createElement
    // It does not include string, number, null, undefined
    type ReactElement = {
      type: string | ComponentType;
      props: any;
      key: string | null;
    };
    

    Como regla general: utiliza ReactNode como valor por defecto para la propiedad children a menos que tengas una razón concreta para restringirlo.

    // Most flexible: use ReactNode for children in most cases
    interface CardProps {
      children: ReactNode;
    };
    
    // And use ReactElement when the requirements are not very flexible
    interface StrictWrapperProps {
      children: ReactElement;
    };
    

    Vale la pena explicar con más precisión la diferencia entre ReactNode y ReactElement.

    Un ReactNode puede ser cualquiera de los siguientes:

    const example1 = <div>Hello</div>;        // ReactElement ✅
    const example2 = "Hello";                 // string ✅
    const example3 = 42;                      // number ✅
    const example4 = true;                    // boolean ✅ (renders nothing)
    const example5 = null;                    // null ✅ (renders nothing)
    const example6 = undefined;               // undefined ✅ (renders nothing)
    const example7 = [<div />, <span />];     // array of ReactElements ✅
    
    // All of the above are ReactNode
    

    Por otro lado, un ReactElement está más restringido:

    const example1 = <div>Hello</div>;
    const example2 = <Button onClick={someHandlerFn}>Submit</Button>;
    const example3 = React.createElement('div', null, 'Hello');
    
    // Under the hood, every ReactElements look like this:
    {
      type: 'div',
      props: { children: 'Hello' },
      key: null,
    }
    

    El error: El tipo 'X' no se puede asignar al tipo 'ReactNode'.

    Esto suele ocurrir cuando intentas renderizar un valor que TypeScript no reconoce como contenido renderizable válido.

    // Objects are not a valid React children
    interface User {
      name: string;
      email: string;
    }
    
    
    // Then if you try to do this
    function DisplayUser({ user }: { user: User }) {
      return <div>{user}</div>;
    }
    
    // It'll probably give you an error that looks something like...
    // Error: Type 'User' is not assignable to type 'ReactNode'
    // Objects are not valid React Children
    

    La solución: renderiza los campos individuales en lugar del objeto completo.

    function DisplayUser({ user }: { user: User }) {
      return (
        <div>
          <p>{user.name}</p>
          <p>{user.email}</p>
        </div>
      );
    }
    

    El error: El tipo de elemento JSX 'X' no tiene ninguna firma de construcción ni de llamada.

    Esto puede resultar confuso al principio. Aparece cuando pasas un valor a algún lugar esperando que se comporte como un componente, pero TypeScript no tiene forma de confirmar si realmente lo es.

    // TypeScript doesn't know 'icon' is a valid component
    interface ButtonProps {
      icon: object; // too vague
    };
    
    // So when you try this...
    function Button({ icon: Icon }: ButtonProps) {
      return <Icon />;
    }
    
    // The above code will give you the error
    // Error: JSX element type 'Icon' does not have any construct
    // or call signatures
    

    La solución: tipa los componentes dinámicos con React.ComponentType:

    import { ComponentType } from 'react';
    
    interface ButtonProps {
      icon: ComponentType;
    };
    
    function Button({ icon: Icon }: ButtonProps) {
      return (
        <button>
          <Icon />
        </button>
      );
    }
    
    // TypeScript now knows Icon is a valid component
    

    Si ese componente también recibe props, puedes describirlos explícitamente:

    interface IconProps {
      size?: number;
      color?: string;
    };
    
    interface ButtonProps {
      icon: ComponentType<IconProps>;
    };
    
    function Button({ icon: Icon }: ButtonProps) {
      return (
        <button>
          <Icon size={12} color="white" />
        </button>
      );
    }
    
    // So now the props are typed too
    

    Un atajo útil que vale la pena conocer es el tipo de utilidad incorporado en React, PropsWithChildren. Agrega automáticamente children?: ReactNode a cualquier interfaz de props que lo rodee, evitando que tengas que declarar ese campo cada vez:

    import { PropsWithChildren } from 'react';
    
    // Now instead of doing this
    interface CardProps {
      title: string;
      children?: ReactNode;
    };
    
    // You can do this
    type CardProps = PropsWithChildren<{ title: string }>;
    
    // So with this, instead of explicitly typing children manually,
    // you can use the above code
    
    function Card({ title, children }: CardProps) {
      return (
        <div>
          <h1>{title}</h1>
          <div>{children}</div>
        </div>
      );
    }
    

    El problema: renderizar listas de elementos

    TypeScript impone reglas estrictas en relación con las claves en las listas renderizadas, pero la mayoría de los errores de tipo que encontrarás aquí provienen en realidad de cómo están tipificados los propios elementos, no de las claves.

    // Items might be undefined, or item.id might not be a valid key type
    function ItemList({ items }: { items: Item[] | undefined }) {
      return (
        <ul>
          {
            items.map(item => (
              <li key={item.id}>{item.name}</li>
            ))
          }
        </ul>
      );
    }
    
    // Error: Object is possibly 'undefined'
    

    La solución: protegerse contra valores undefined y asegurarse de que la tipificación esté correcta:

    function ItemList({ items = [] }: { items?: Item[] }) {
      return (
        <ul>
          {
            items.map((item) => (
              <li key={item.id}>{item.name}</li>
            ))
          }
        </ul>
      );
    }
    

    Para terminar…

    Los errores de TypeScript dejan de parecer obstáculos en cuanto comienzas a tratarlos como pistas. Ese cambio ocurre en el momento en que dejas de reaccionar con “uff, otra vez no” y en su lugar empiezas a preguntarte “¿qué es lo que TypeScript realmente está tratando de decirme aquí?”

    Los errores que se tratan en esta guía son aquellos con los que seguirás topándote a lo largo de tu trabajo con React y TypeScript. Lo que diferencia a un desarrollador que lucha constantemente contra el sistema de tipos de aquel que trabaja cómodamente con él suele reducirse al reconocimiento de patrones, nada más. A estas alturas ya conoces esos patrones.

    Lectura relacionada

  • La verdadera complejidad del JavaScript moderno proviene de las herramientas, no del idioma — Este artículo explica cómo las características fundamentales de JavaScript como async/await y el encadenamiento opcional simplifican el código, mientras que las herramientas y dependencias excesivas generan una complejidad innecesaria.