Saltar al contenido principal

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:

  1. Uso de async/await: Obligatorio para manejar Promesas. Más legible.
  2. Manejo de Errores: Usar try/catch en funciones async para capturar rechazos.
  3. Evitar Callbacks Anidados: Prohibido. Usar Promesas y async/await.

Contexto y Justificación

async/await simplifica código asíncrono. try/catch explícito es crucial para robustez.

Ejemplos y Contraejemplos

  • Correcto (async/await con try/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/catch o con .then anidados):
    // 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:

  1. Tipado Estático Obligatorio: Usar tipos explícitos (params, retorno, variables) si TypeScript no infiere de forma segura/obvia.
  2. Prohibición de any: Prohibido any implícito/explícito (noImplicitAny, strictNullChecks activados). Usar unknown y type narrowing.
  3. 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. any anula la seguridad; unknown es 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:

  1. interface: Para definir forma de objetos o contrato de clases (implements). Preferible para "qué es" un objeto. Soporta declaration merging.
  2. type: Para alias (primitivos, uniones |, intersecciones &, tuplas), tipos complejos (mapeados, condicionales), o tipos que no deben extenderse.
  3. 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. interface para OOP. type para flexibilidad/alias. enum para 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

interface vs type: Consistencia es clave, pero type obligatorio para uniones/intersecciones/complejos, interface para 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.