TSX y el Auge de los Componentes Frontend Tipados
Estás construyendo un componente React. Pasas un string donde debería ir un número. TypeScript lo detecta antes que el navegador. Esta simple interacción—tipos que previenen errores en tiempo de ejecución—explica por qué TSX se ha convertido en el formato predeterminado para el desarrollo frontend moderno.
Este artículo cubre qué significan realmente los componentes frontend tipados en la práctica: tipado de props, manejo de eventos, patrones de children, y cómo la inferencia de TypeScript elimina código repetitivo. También abordaremos cómo la división de componentes de servidor y cliente de React 19 afecta tu estrategia de tipado.
Puntos Clave
- Los componentes tipados TSX detectan discrepancias de tipos en tiempo de compilación, reduciendo errores en tiempo de ejecución antes de que el código llegue al navegador.
- La inferencia de TypeScript maneja automáticamente la mayoría del tipado de eventos—las anotaciones explícitas solo son necesarias al extraer handlers a funciones separadas.
- Las uniones discriminadas permiten componentes type-safe con comportamientos mutuamente exclusivos, donde TypeScript reduce los tipos basándose en una propiedad discriminante.
- El límite servidor/cliente de React 19 introduce restricciones de serialización: las props pasadas de componentes de servidor a cliente deben ser serializables (sin funciones, clases, símbolos u otros valores no serializables).
Qué Significan Realmente los Componentes Tipados TSX
Un componente tipado no es solo un componente con props tipadas. Es un componente donde TypeScript entiende el contrato completo: qué entra, qué sale, y qué sucede en el medio.
Con la transformación JSX moderna (react-jsx), no importas React en cada archivo. Escribes TSX, y la cadena de herramientas se encarga del resto. Vite configura esto correctamente desde el inicio:
interface ButtonProps {
label: string
disabled?: boolean
}
function Button({ label, disabled = false }: ButtonProps) {
return <button disabled={disabled}>{label}</button>
}
Observa que no hay React.FC aquí. Ese patrón es opcional—y a menudo innecesario. Tipar las props directamente en el parámetro de la función es más limpio y evita la prop implícita children que React.FC añade la quieras o no.
Tipado de Props y Seguridad de Tipos en React TSX
El tipado de props es donde la mayoría de los desarrolladores comienzan, pero la seguridad de tipos en React TSX va más profundo. Considera las uniones discriminadas para componentes con comportamientos mutuamente exclusivos:
type LinkButtonProps =
| { variant: 'button'; onClick: () => void }
| { variant: 'link'; href: string }
function LinkButton(props: LinkButtonProps) {
if (props.variant === 'link') {
return <a href={props.href}>Navigate</a>
}
return <button onClick={props.onClick}>Click</button>
}
TypeScript reduce el tipo basándose en variant. No puedes acceder accidentalmente a href cuando variant es 'button'. Este patrón escala bien para APIs de componentes complejas.
Tipado de Eventos Sin Código Repetitivo
Los manejadores de eventos a menudo confunden a los desarrolladores nuevos en TypeScript. La buena noticia: la inferencia maneja la mayoría de los casos automáticamente.
import { useState } from 'react'
function SearchInput() {
const [query, setQuery] = useState('')
// TypeScript infiere el tipo de evento desde el contexto
return (
<input
value={query}
onChange={(e) => setQuery(e.currentTarget.value)}
/>
)
}
Cuando extraes handlers a funciones separadas, necesitarás tipos explícitos:
import type { ChangeEvent } from 'react'
function handleChange(event: ChangeEvent<HTMLInputElement>) {
// event.currentTarget.value está correctamente tipado como string
}
Discover how at OpenReplay.com.
Tipado de Children: ReactNode vs ReactElement
Para children, React.ReactNode cubre el caso común—cualquier cosa renderizable:
import type { ReactNode } from 'react'
interface CardProps {
children: ReactNode
}
Usa React.ReactElement cuando necesites estrictamente elementos JSX, excluyendo strings y números. Para componentes que aceptan children, PropsWithChildren es una alternativa más limpia que añadir manualmente la prop children:
import type { PropsWithChildren } from 'react'
interface CardProps {
title: string
}
function Card({ title, children }: PropsWithChildren<CardProps>) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
)
}
Tipado de Componentes de Servidor y Cliente en React 19
Los proyectos TypeScript de React 19, especialmente aquellos que usan Next.js App Router, dividen los componentes en límites de servidor y cliente. Esto afecta el tipado.
Los componentes de servidor pueden ser asíncronos y obtener datos directamente:
// Server Component - no necesita directiva (predeterminado en App Router)
async function UserProfile({ userId }: { userId: string }) {
const user = await fetchUser(userId)
return <div>{user.name}</div>
}
Los componentes de cliente requieren la directiva 'use client' y pueden usar hooks:
'use client'
import { useState } from 'react'
interface CounterProps {
initialCount: number
}
function Counter({ initialCount }: CounterProps) {
const [count, setCount] = useState(initialCount)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
El límite de tipado importa: las props pasadas de componentes de servidor a cliente deben ser serializables. Funciones, clases, símbolos y valores no serializables (como instancias de clases o Dates) no cruzarán ese límite.
Acciones de Formulario y Flujos de Trabajo Tipados Modernos
React 19 introduce server actions (comúnmente usadas con formularios) como un patrón tipado para manejar envíos, típicamente definidas en archivos de servidor e invocadas desde componentes de cliente:
async function submitForm(formData: FormData) {
'use server'
const email = formData.get('email')
if (typeof email !== 'string') {
throw new Error('Email is required')
}
// Procesar envío
}
Esto se integra con componentes frontend tipados manteniendo el manejo de formularios consciente de tipos desde el cliente hasta el servidor.
Conclusión
Los componentes tipados TSX reducen bugs al detectar discrepancias en tiempo de compilación. La inferencia de TypeScript minimiza el código repetitivo—rara vez necesitas anotaciones de tipo explícitas para hooks o handlers inline. La división servidor/cliente en React 19 añade una nueva consideración: límites de serialización.
Omite React.FC a menos que tengas una razón específica. Tipa las props directamente. Deja que la inferencia funcione. Tu editor te lo agradecerá con autocompletado preciso y retroalimentación inmediata de errores.
Preguntas Frecuentes
Tipa las props directamente en los parámetros de función. React.FC añade una prop children implícita la quieras o no, y tipar las props directamente es más limpio. El equipo de React no recomienda React.FC como el patrón predeterminado. Úsalo solo cuando necesites específicamente su comportamiento, como al trabajar con bases de código legacy que lo esperan.
Para handlers inline, TypeScript infiere el tipo de evento automáticamente desde el contexto. Cuando extraes handlers a funciones separadas, usa tipos explícitos como React.ChangeEvent para cambios en inputs o React.MouseEvent para clics. El parámetro genérico especifica el tipo de elemento, como HTMLInputElement o HTMLButtonElement.
ReactNode acepta cualquier cosa renderizable incluyendo strings, números, null, undefined y elementos JSX. ReactElement acepta solo elementos JSX, excluyendo primitivos. Usa ReactNode para la mayoría de los casos. Usa ReactElement cuando tu componente requiera específicamente children JSX y deba rechazar texto plano o números.
No. Las props pasadas de componentes de servidor a cliente deben ser serializables. Funciones, clases, símbolos y otros valores no serializables no pueden cruzar el límite servidor-cliente. Define manejadores de eventos y callbacks dentro de componentes de cliente, o usa server actions para envíos de formularios que necesiten procesamiento del lado del servidor.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.