From n00b to ZeroCool / Profesionalización

JavaScript vs TypeScript: la batalla por el tipado (sin humo)

Elige entre JavaScript y TypeScript con casos reales, ruta de migración incremental, errores comunes y checklist para tu frontend.

Lo que vale la pena leer aquí

Ayer, 6:47 pm. Ya estabas pensando en cerrar la laptop (de esas ya medio lentas, con el ventilador sonando como dron) cuando cae el mensaje en Slack/WhatsApp:

Ayer, 6:47 pm. Ya estabas pensando en cerrar la laptop (de esas ya medio lentas, con el ventilador sonando como dron) cuando cae el mensaje en Slack/WhatsApp:

“Oye, en production el botón de pagar ya no abre el modal… ¿tú moviste algo?”

Tú jurabas que no. Tu pull request fue “chiquito”: que una función aceptara un objeto “más completo”. En JavaScript, eso puede significar cualquier cosa. Y cuando truena, no hay abrazo. Hay presión, hay clientes, y hay un undefined viéndote directo a los ojos.

Ahí está la batalla del tipado: no es religión ni estatus. Es cuánto riesgo te avientas por moverte rápido… y cuánto te cuesta el rollback cuando el jefe pide el fix “en corto”.

Qué vas a sacar de aquí

  • Qué problema resuelve de verdad TypeScript (y qué no).
  • Cómo decidir JavaScript vs TypeScript según proyecto, equipo y deadline.
  • Una ruta realista para migrar sin quemar el sprint: allowJs, checkJs, tsconfig, strict por etapas.
  • Ejemplos de bugs comunes que el tipado detecta antes del deploy.
  • Errores típicos (los que hacen que la banda odie TS) y cómo evitarlos.

El tipado no te hace “más pro”: te baja la incertidumbre

JavaScript es flexible. Esa flexibilidad es poder… y también es caos cuando hay prisa.

TypeScript no reemplaza JavaScript: le pone un sistema de tipos estático que vive en tu editor y en CI. En runtime, el navegador o Node siguen corriendo JavaScript normal.

La decisión, en la vida real, suele verse así:

  • Equipo chico + proyecto corto: JS te da velocidad.
  • Producto que va a vivir meses/años + más gente metiendo mano: TS paga intereses.
  • Freelance mal cotizado con soporte “cuando se pueda”: TS es como etiquetar cables antes de cerrar el rack. Te salva cuando regresas tres meses después y ya no te acuerdas ni qué era ese “hotfix rápido”.

Dos escenas que se repiten:

  1. Onboarding con docs “en Notion” que nadie actualiza. Con TS, el código explica más cosas y reduce el “oye, ¿esto qué recibe?” cada 10 minutos.
  2. Deploy tarde con CI lento y alguien compilando desde una laptop que ya pide retiro. TS puede sumar tiempo si lo metes a lo bruto, pero también te ahorra horas de debugging por bugs bien tontos.

Cómo decidir entre JavaScript y TypeScript sin pelearte con el equipo

1) Mide el riesgo: ¿qué tan caro es un bug?

Preguntas netas:

  • ¿Este frontend mueve lana? (checkout, suscripción, pagos, logística)
  • ¿Hay formularios con datos “raros” o incompletos?
  • ¿Integras APIs que cambian seguido?
  • ¿Hay rotación de devs o se sube banda nueva cada mes?

Si respondiste “sí” a 2 o más, TypeScript normalmente se paga rápido.

2) Revisa tu superficie de datos: ¿cuánta info viaja entre capas?

TS brilla cuando hay muchos objetos cruzando fronteras:

  • API -> normalizador -> state -> UI
  • forms -> validation -> submit
  • feature flags -> permisos -> rutas

En una landing estática o un micrositio con 2 componentes, TS puede sentirse como cargar maleta para ir al Oxxo.

3) El “strictness” es una perilla, no un switch

El error clásico: creer que TS es “estricto o nada” o que la migración tiene que ser big-bang.

No. Bien usado, TypeScript es incremental. Y esa decisión (hacerlo por etapas) define si el workflow se vuelve más fluido o si todos lo van a esquivar con any.

Paso a paso: migrar de JavaScript a TypeScript sin romper todo

La meta no es “convertir todo a TS en una semana”. La meta es bajar bugs y subir claridad sin frenar el delivery.

Paso 0: Alinea expectativas (esto también es parte del setup)

Antes de tocar config, pónganse de acuerdo:

  • ¿Qué queremos ganar? (menos bugs, mejor DX, refactors más seguros)
  • ¿Qué no queremos? (bloquear merges por tipos imposibles)
  • ¿Qué migramos primero? (lo más crítico, lo que más se rompe, o lo más aislado)

Si no se habla, TS se vuelve “otra regla” y lo van a duct-tapear con any.

Paso 1: Instala TypeScript y arranca con un tsconfig amable

npm i -D typescript
npx tsc --init

Un tsconfig.json inicial que no te incendie el backlog:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": false,
    "noImplicitAny": false,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Decisión práctica: arranca con strict: false y sube el nivel por etapas. Avanzar 20% consistente vale más que “100%” con trauma y abandono.

Paso 2: Migración incremental con allowJs + checkJs

Si tu base está en JS, deja que convivan:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false
  },
  "include": ["src"]
}
  • allowJs: true permite que TS lea .js.
  • checkJs: false evita que te caiga una avalancha de warnings desde el día 1.

Cuando ya quieras valor sin convertir todo a .ts:

{
  "compilerOptions": {
    "checkJs": true
  }
}

Y sí, // @ts-nocheck existe, pero úsalo como curita: solo donde de plano no puedes todavía, y con caducidad.

Paso 3: Empieza donde hay ROI: API, modelos y fronteras

El bug típico: “la API cambió un campo y nadie se enteró”.

Ejemplo bien real:

// JS
function formatUser(u) {
  return u.profile.name.toUpperCase();
}

Si la API manda profile: null para usuarios incompletos, truena en runtime.

En TS, el código te obliga a decidir:

type User = {
  id: string;
  profile: null | {
    name: string;
  };
};

function formatUser(u: User) {
  if (!u.profile) return "(sin perfil)";
  return u.profile.name.toUpperCase();
}

Decisión práctica: tipa primero lo que cruza fronteras (API, storage, eventos, props). Ahí es donde más duele y más se rompe.

Paso 4: Migra por “islas”: .ts y .tsx

No conviertas todo el árbol de jalón. Haz islas:

  1. src/api/ (clientes, DTOs)
  2. src/utils/ (helpers de fecha, money, parsing)
  3. Componentes críticos (checkout, login, forms)

Regla que sirve: cada vez que toques un archivo por una feature, decide si conviene convertirlo en ese momento. Si sí, conviértelo. Si no, no te castigues.

Paso 5: Sube la perilla: strict por etapas

Cuando ya tengas base, el salto grande es strict: true. Pero se puede hacer gradual (por paquetes, carpetas o con reglas de lint).

Una ruta simple:

  • Semana 1-2: strict: false + tipado en modelos/API.
  • Semana 3: noImplicitAny: true para forzar decisiones.
  • Semana 4+: strict: true en lo core (y tolerancia controlada en legacy).

Paso 6: Deja de pelearte con el editor: ESLint + typings bien puestos

Si usas React/Vue/Next, agrega los typings que toquen (depende del stack). La idea es que el editor no te “mienta” con autocompletados chuecos.

Y ojo: no normalices silenciar TS por sistema.

  • any es un préstamo con intereses.
  • unknown es un “alto” razonable: primero validas, luego usas.

Ejemplo:

function parsePrice(value: unknown): number {
  if (typeof value === "number") return value;
  if (typeof value === "string") return Number(value.replace(/[^0-9.]/g, ""));
  return 0;
}

Esto pasa diario con forms o APIs que regresan cosas raras. TS te empuja a tratarlo como es, no como quisieras que fuera.

JavaScript vs TypeScript: la batalla por el tipado (sin humo) - visual explicativa 1
Visual de apoyo: Qué vas a sacar de aquí

Ideas de screenshots (para que se entienda sin leer tanto)

  • VS Code mostrando autocompletado y un error de tipo en una prop (Property 'x' does not exist on type...).
  • Diff de un PR cambiando .js -> .ts y agregando tipos a la respuesta del API.
  • Error típico en build (Object is possibly 'null') y cómo lo resuelves con un guard.

Errores comunes (para no terminar odiando TypeScript)

1) “Metimos TS y ahora todo es any

Síntoma: el repo “ya es TS”, pero nadie confía en los tipos.

Cómo salir:

  • Bloquea any en código nuevo con ESLint o una convención clara.
  • Permite any solo con comentario justificado y ticket.
  • Prefiere unknown + validación.

2) Prender strict desde el día 1 en un legacy grande

Síntoma: 1,800 errores, el equipo se paraliza, se abandona la iniciativa.

Cómo salir: gradual. Si hay que venderlo, mide tiempo de debugging o bugs regresivos antes/después.

3) Tipar React/Vue como si estuvieras escribiendo Java

Síntoma: genéricos imposibles, tipos kilométricos, DX horrible.

Cómo salir: tipos “suficientes”. Tipa fronteras y props importantes. Si un tipo parece tesis, probablemente estás forzando de más.

4) Creer que TS valida runtime

Síntoma: “pero el tipo dice que siempre viene email”… y en runtime no viene.

Cómo salir: TS no valida runtime. Para inputs externos, usa validación (Zod, Valibot, Yup, lo que uses) o type guards.

Ejemplo con Zod:

import { z } from "zod";

const UserSchema = z.object({
  id: z.string(),
  profile: z.object({ name: z.string() }).nullable()
});

type User = z.infer<typeof UserSchema>;

function parseUser(data: unknown): User {
  return UserSchema.parse(data);
}

5) “Los tipos están bonitos, pero el build se hizo lento”

Síntoma: CI tarda más y el dev server se siente pesado.

Cómo salir:

  • Deja que el bundler transpile (SWC/esbuild) y corre el type-check aparte.
  • En CI usa tsc --noEmit.
  • Si el repo ya creció (monorepo), considera project references.
JavaScript vs TypeScript: la batalla por el tipado (sin humo) - visual explicativa 2
Visual de apoyo: El tipado no te hace “más pro”: te baja la incertidumbre

Checklist final (para decidir y para ejecutar)

  • ¿Tu app tiene flujos críticos (pagos, auth, data sensible)? Si sí, TS casi siempre.
  • ¿Tu equipo crece o rota? TS ayuda en onboardings y refactors.
  • ¿Puedes migrar por islas? Empieza por API/models.
  • ¿Tu tsconfig es amable (strict gradual)?
  • ¿Tienes regla para any (no usarlo “porque sí”)?
  • ¿Validas runtime para inputs externos (forms/APIs)?
  • ¿Separaste type-check del build para performance?
  • ¿Tienes Definition of Done para archivos migrados (sin @ts-ignore gratis)?

FAQ

1) ¿TypeScript hace mi app más rápida?

No por sí mismo. TypeScript se borra al compilar. Lo que sí hace es bajar loops de debugging, y eso te vuelve más rápido a ti y al equipo.

2) ¿Cuándo me conviene quedarme en JavaScript?

Cuando el proyecto es corto, el dominio es simple (poca data) y el costo de un bug es bajo: landing, prototipo, demo para validar.

3) ¿Qué tan pesado es migrar un proyecto grande?

No es “difícil”; es talacha. La clave es incremental, empezar por fronteras (API/models) y no prender strict a lo bruto.

4) ¿Qué hago con librerías sin tipos?

Busca primero @types/paquete. Si no existe, crea un global.d.ts con typings mínimos o encapsula la librería en un wrapper tipado para que el resto del código no sufra.

5) ¿any está prohibido?

No. Pero debe ser excepción, no estrategia. Si usas any como duct tape, en 2-3 meses vas a estar de regreso en JavaScript… solo que más lento.

Siguiente episodio

El tipado es solo el primer sable láser. El siguiente round: elegir framework sin casarte a ciegas.

React, Vue y Svelte se ven parecidos desde lejos, pero en chamba real pesan otras cosas: equipo, performance, curva de aprendizaje y mantenimiento.