Web Components: El Futuro de la Reutilización de Código
Crea componentes reutilizables sin frameworks con Web Components y Shadow DOM.
El 73% de los desarrolladores JavaScript reporta que TypeScript reduce significativamente el número de bugs en producción. Si aún no has migrado tus proyectos a TypeScript, estás dejando dinero sobre la mesa.
JavaScript fue diseñado en 10 días. Es increíblemente flexible, pero esa flexibilidad tiene un precio:
// JavaScript: Esto es válido (y peligroso)
const user = { name: "John" };
user.age.toUpperCase(); // ¡CRASH en producción!
const result = "5" + 2; // Es "52", no 7
const total = null + 10; // Es 10 (!?)
Estos errores pasan desapercibidos hasta que los usuarios los encuentran en producción.
Con TypeScript, estos errores se detectan antes de ejecutar el código:
// TypeScript: Error detectado inmediatamente
interface User {
name: string;
age?: number;
}
const user: User = { name: "John" };
user.age.toUpperCase(); // ❌ Error: age podría ser undefined
const result: number = "5" + 2; // ❌ Error: esperaba number, obtuvo string
// Código tipado es autodocumentado
function calculateDiscount(
price: number,
percentage: number,
applyCoupon: boolean
): number {
// Solo acepta estos tipos, nada más
const discount = (price * percentage) / 100;
return applyCoupon ? discount * 1.5 : discount;
}
// Imposible llamar incorrectamente
calculateDiscount(100, "20%", true); // ❌ Error: "20%" no es number
calculateDiscount("100", 20, true); // ❌ Error: "100" no es number
// Interfaces: Para definir contratos
interface Payment {
amount: number;
currency: "USD" | "EUR" | "MXN";
processed: boolean;
}
// Types: Para tipos más complejos
type PaymentStatus = "pending" | "completed" | "failed" | "refunded";
type PaymentRecord = {
id: string;
payment: Payment;
status: PaymentStatus;
timestamp: Date;
};
// Sin genéricos: Poco reutilizable
function getFromArray(arr: any[], index: number): any {
return arr[index];
}
// Con genéricos: Totalmente tipado y reutilizable
function getFromArray<T>(arr: T[], index: number): T {
return arr[index];
}
const numberValue = getFromArray<number>([1, 2, 3], 0); // number
const stringValue = getFromArray<string>(["a", "b"], 1); // string
interface User {
id: number;
name: string;
email: string;
password: string;
role: "admin" | "user";
}
// Partial: Todos los campos opcionales
type PartialUser = Partial<User>;
// Pick: Solo ciertos campos
type UserPublic = Pick<User, "id" | "name" | "email">;
// Omit: Todos excepto ciertos campos
type UserWithoutPassword = Omit<User, "password">;
// Record: Objeto con claves específicas
type UserPermissions = Record<"read" | "write" | "delete", boolean>;
// types/api.ts
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
timestamp: number;
}
export interface Product {
id: string;
name: string;
price: number;
stock: number;
category: string;
}
// services/productService.ts
async function fetchProducts(): Promise<ApiResponse<Product[]>> {
const response = await fetch('/api/products');
const data: ApiResponse<Product[]> = await response.json();
if (!data.success) {
throw new Error(data.error || 'Unknown error');
}
return data;
}
// Uso: TypeScript sabe exactamente qué esperar
const products = await fetchProducts();
// products.data es Product[] | undefined
products.data?.forEach(p => {
console.log(p.name); // ✓ Autocompletar funciona
console.log(p.invalidField); // ❌ Error: invalidField no existe
});
// components/UserCard.tsx
interface UserCardProps {
user: {
id: number;
name: string;
avatar: string;
};
onSelect: (userId: number) => void;
isSelected?: boolean;
}
export const UserCard: React.FC<UserCardProps> = ({
user,
onSelect,
isSelected = false,
}) => {
return (
<div onClick={() => onSelect(user.id)} className={isSelected ? 'selected' : ''}>
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
</div>
);
};
// Uso correcto
<UserCard
user={{ id: 1, name: "John", avatar: "..." }}
onSelect={(id) => console.log(id)} // ✓ onSelect recibe number
isSelected={true}
/>
// Esto genera errores:
<UserCard
user={{ id: "1", name: "John" }} // ❌ id debe ser number
onSelect={(id: string) => {}} // ❌ onSelect espera (id: number) => void
/>
# Instalar dependencias
npm install --save-dev typescript @types/node @types/react
# Crear tsconfig.json
npx tsc --init
# Configurar build process
npm install --save-dev ts-loader webpack
// tsconfig.json - Máximo rigor
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
any temporalmente para código complejoany progresivamentenpm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm install --save-dev prettier
# .github/workflows/typecheck.yml
name: Type Check
on: [push, pull_request]
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm ci
- run: npx tsc --noEmit
any// Malo: Pierdes el propósito de TypeScript
function processData(data: any): any {
return data.something.nested.value;
}
// Bueno: Define tipos reales
interface DataStructure {
something: {
nested: {
value: string;
};
};
}
function processData(data: DataStructure): string {
return data.something.nested.value;
}
@ts-ignore// Malo: Esto es una deuda técnica
// @ts-ignore
const value = something.impossibleProperty;
// Bueno: Arregla el problema real
type Something = {
possibleProperty: string;
};
// Malo: Reinventar la rueda
const userInput: any = document.getElementById('user');
// Bueno: Usar tipos del DOM
const userInput: HTMLInputElement = document.getElementById(
'user'
) as HTMLInputElement;
| Métrica | Antes | Después | Mejora |
|---|---|---|---|
| Bugs en QA por sprint | 28 | 6 | -78% |
| Tiempo de debugging | 120 hrs | 35 hrs | -71% |
| Refactoring seguro | No | Sí | +∞ |
| Velocidad de codificación | 100% | 115% | +15% |
| Satisfacción del equipo | 6.2/10 | 8.1/10 | +30% |
TypeScript no es un lujo, es una inversión en calidad. El costo inicial de aprendizaje se recupera rápidamente en menos bugs, código más mantenible y equipos más productivos.
Si trabajas en proyectos medianos o grandes, TypeScript es obligatorio. El futuro del JavaScript tipado ya está aquí.
Próximo paso: Comienza con un proyecto pequeño, experimenta con TypeScript durante 2 semanas y verás por qué miles de empresas lo usan hoy.
Recursos:
Profundiza en temas de arquitectura y rendimiento.