JavaScript Moderno
6. Reglas de Módulos (ESM vs CommonJS)
Regla Principal
Regla: Usar exclusivamente módulos ES6 (
import/export). Prohibido CommonJS (require/module.exports) en código nuevo, salvo interoperabilidad inevitable y aislada con herramientas legacy.
Contexto y Justificación
ESM es el estándar moderno, soportado nativamente. Ofrece carga asíncrona, análisis estático (tree-shaking) y sintaxis más clara. CommonJS es obsoleto para nuevo desarrollo.
Ejemplos y Contraejemplos
- Correcto (ESM):
// utils/math.ts
export function sumar(a: number, b: number): number { return a + b; }
export const PI = 3.14;
export default function mult(a: number, b: number): number { return a * b; }
// main.ts
import mult, { sumar as add, PI as PI_VAL } from './utils/math';
import * as mathUtils from './utils/math';
console.log(add(1, 2)); // 3
console.log(mult(2, 3)); // 6
console.log(mathUtils.PI); // 3.14 - Incorrecto (CommonJS):
// legacy/utils.js
function dividir(a, b) { return a / b; }
module.exports = { dividir: dividir }; // INCORRECTO
// app.js
const utils = require('./legacy/utils'); // INCORRECTO
console.log(utils.dividir(4, 2)); // 2
Cuándo Aplicar
Todo código nuevo JS/TS.
Cuándo Evitar o Flexibilizar
Solo para interactuar con archivos de config legacy o APIs Node.js muy antiguas que solo expongan CommonJS. Aislar y documentar.
7. Reglas de Manejo de Asincronía (Promesas y Async/Await)
Regla Principal
Regla:
- Uso de
async/await: Obligatorio para manejar Promesas. Más legible.- Manejo de Errores: Usar
try/catchen funcionesasyncpara capturar rechazos.- Evitar Callbacks Anidados: Prohibido. Usar Promesas y
async/await.
Contexto y Justificación
async/awaitsimplifica código asíncrono.try/catchexplícito es crucial para robustez.
Ejemplos y Contraejemplos
- Correcto (
async/awaitcontry/catch):async function fetchData(url: string): Promise<any> {
try {
const response = await fetch(url);
if (!response.ok) {
console.error(`HTTP error: ${response.status}`);
return null;
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error);
return null;
}
} - Incorrecto (Sin
try/catcho con.thenanidados):// INCORRECTO: Sin manejo de errores
async function fetchDataInseguro(url: string) {
const response = await fetch(url);
const data = await response.json(); // Puede fallar
return data;
}
// INCORRECTO: Callback hell potencial
function fetchDataThen(url: string) {
fetch(url).then(res => res.json()).then(data => { /*...*/ }).catch(err => { /*...*/ });
}
Cuándo Aplicar
Siempre con operaciones asíncronas que devuelvan Promesas (fetch, I/O Node.js, etc.).
Cuándo Evitar o Flexibilizar
No flexibilizar. Para paralelismo (
Promise.all, etc.), la estructura varía pero el manejo de errores sigue siendo obligatorio.
TypeScript Aplicado
8. Reglas de Tipado Estático y Dinámico (TypeScript)
Regla Principal
Regla:
- Tipado Estático Obligatorio: Usar tipos explícitos (params, retorno, variables) si TypeScript no infiere de forma segura/obvia.
- Prohibición de
any: Prohibidoanyimplícito/explícito (noImplicitAny,strictNullChecksactivados). Usarunknowny type narrowing.- Tipos Específicos: Preferir tipos específicos (literales, uniones, enums) sobre primitivos genéricos (
string,number).
Contexto y Justificación
Tipado estático detecta errores temprano, mejora autodocumentación y herramientas.
anyanula la seguridad;unknownes la alternativa segura. Tipos específicos mejoran precisión.
Ejemplos y Contraejemplos
- Correcto:
function saludar(nombre: string): string { return `Hola, ${nombre}`; }
function procesar(input: unknown): void {
if (typeof input === 'string') { console.log(input.toUpperCase()); }
else if (typeof input === 'number') { console.log(input.toFixed(2)); }
}
type Estado = 'PENDIENTE' | 'EXITO' | 'ERROR';
let estadoActual: Estado = 'PENDIENTE'; - Incorrecto:
function sumar(a, b) { return a + b; } // INCORRECTO: Sin tipos
function procesarAny(data: any): void { data.metodoInexistente(); } // INCORRECTO: any
let estadoStr: string = 'pending'; // INCORRECTO: string genérico
Cuándo Aplicar
Todo código TypeScript.
Cuándo Evitar o Flexibilizar
any: Prohibido, salvo rarísimas excepciones justificadas y encapsuladas. Inferencia: OK si es segura y obvia, pero explícito en params/retorno.
9. Reglas sobre Interfaces, Tipos y Enums (TypeScript)
Regla Principal
Regla:
interface: Para definir forma de objetos o contrato de clases (implements). Preferible para "qué es" un objeto. Soporta declaration merging.type: Para alias (primitivos, uniones|, intersecciones&, tuplas), tipos complejos (mapeados, condicionales), o tipos que no deben extenderse.enum: Para conjuntos finitos de constantes nombradas (estados, roles). Preferir string enums (enum Rol { Admin = 'ADMIN' }) por legibilidad.
Contexto y Justificación
Uso consistente mejora legibilidad/mantenibilidad.
interfacepara OOP.typepara flexibilidad/alias.enumpara constantes seguras.
Ejemplos y Contraejemplos
- Correcto:
interface Usuario { id: number; nombre: string; readonly email?: string; }
interface Autenticable { autenticar(): boolean; }
class Sistema implements Autenticable { autenticar() { return true; } }
type ID = number | string;
type Callback = (data: any) => void;
type Coordenada = [number, number];
enum EstadoPedido { Pendiente = 'PENDIENTE', Enviado = 'ENVIADO' }
function procesarPedido(id: ID, estado: EstadoPedido) { /*...*/ } - Incorrecto:
type PersonaType = { nombre: string; edad: number; }; // INCORRECTO: Mejor interface
// interface IdInterfaz = number | string; // INCORRECTO: Syntax error
const ROL_ADMIN = 'admin'; // INCORRECTO: Usar enum
function checkPerm(rol: string) { if (rol === 'admi') {} } // Propenso a typos
Cuándo Aplicar
Al definir estructuras de datos, alias o constantes nombradas.
Cuándo Evitar o Flexibilizar
interfacevstype: Consistencia es clave, perotypeobligatorio para uniones/intersecciones/complejos,interfacepara merging.enum: Usar unión de literales (type Estado = 'A' | 'B') si mapea directamente a valores de API externa.
10. Reglas sobre Tipos Genéricos (TypeScript)
Regla Principal
Regla: Usar genéricos (
<T>) para funciones, clases, interfaces o tipos que operan sobre variedad de tipos de forma segura/reutilizable. Usar restricciones (extends) para limitar tipos permitidos.
Contexto y Justificación
Genéricos permiten código flexible y reutilizable sin sacrificar seguridad (vs
any). Mantienen información de tipo. Restricciones aumentan seguridad.
Ejemplos y Contraejemplos
- Correcto:
function identidad<T>(arg: T): T { return arg; }
const s = identidad('a'); // s es 'a'
const n = identidad(1); // n es 1
class Pila<T> {
private items: T[] = [];
push(item: T) { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
}
const pilaNum = new Pila<number>();
pilaNum.push(1);
const num = pilaNum.pop(); // num es number | undefined
interface ConLongitud { longitud: number; }
function logLong<T extends ConLongitud>(arg: T) { console.log(arg.longitud); }
logLong('abc'); // OK
logLong([1, 2]); // OK
// logLong(5); // Error - Incorrecto:
function idAny(arg: any): any { return arg; } // INCORRECTO: Pierde tipos
const resAny = idAny('a'); // resAny es any
class PilaAny {
private items: any[] = [];
push(item: any) { this.items.push(item); }
pop(): any { return this.items.pop(); } // INCORRECTO: Devuelve any
}
function logLongSinRestriccion<T>(arg: T) {
// console.log(arg.longitud); // INCORRECTO: Error, T no tiene longitud garantizada
}
Cuándo Aplicar
Siempre que se necesite reutilización con seguridad de tipos (colecciones, utilidades, abstracciones).
Cuándo Evitar o Flexibilizar
No evitar si es la solución apropiada. Si solo opera con un tipo concreto, no necesita ser genérico.