La maldición de los parámetros omitidos en TypeScript
\n¿Alguna vez has escrito una función con diez parámetros opcionales y luego has intentado pasar solo el quinto y el décimo? ¿Y de inmediato obtuviste un error de compilación o, peor aún, un error lógico silencioso? ¿Te suena familiar?
\nEn Stack Overflow, esta pregunta ha acumulado miles de visitas. Los desarrolladores de TypeScript tropiezan constantemente con el mismo rastrillo: ¿cómo pasar un parámetro opcional omitiendo otro? Porque la sintaxis estándar no lo permite: los parámetros se pasan por posición, no por nombre.
\nHoy vamos a resolver este dolor de una vez por todas. Te mostraré 4 formas funcionales de solucionar el problema: desde una simple refactorización hasta técnicas avanzadas con objetos parciales. ¡Allá vamos!
\n\n¿Por qué es esto un problema? La maldición posicional
\nEn TypeScript (como en JavaScript), los parámetros de una función se pasan en orden. Si tienes una función:
\nfunction createUser(\n name: string,\n age?: number,\n email?: string,\n isAdmin?: boolean\n) { /* ... */ }\nNo puedes llamarla así: createUser('John', , 'john@mail.com'). Omitir un parámetro deja un hueco que TypeScript no puede digerir. Tendrás que pasar undefined o null, lo que ensucia el código y la lógica.
Pero hay una buena noticia: podemos sortear esta limitación de varias formas elegantes. Analicemos cada una.
\n\nForma 1: Objeto de parámetros (la más correcta)
\nEste es el patrón recomendado en proyectos serios. En lugar de un montón de parámetros posicionales, usamos un solo objeto con campos opcionales:
\ninterface UserOptions {\n name: string;\n age?: number;\n email?: string;\n isAdmin?: boolean;\n}\n\nfunction createUser(options: UserOptions): void {\n const { name, age, email, isAdmin } = options;\n console.log(`Usuario creado ${name}, edad: ${age ?? 'no especificada'}`);\n}\n\n// ¡Ahora podemos pasar solo lo necesario!\ncreateUser({ name: 'Ana', email: 'ana@mail.com' });\ncreateUser({ name: 'Pedro', isAdmin: true });\n\nLas ventajas son obvias:
\n- \n
- No importa cuántos parámetros haya: pasamos solo lo que necesitamos \n
- El código se autodocumenta: se ve inmediatamente qué hace cada cosa \n
- Es fácil añadir nuevos campos sin cambiar la firma \n
Este enfoque resuelve el 90% de los problemas con parámetros opcionales. Pero si trabajas con código heredado o no puedes cambiar la API, sigue leyendo.
\n\nForma 2: Sobrecarga de funciones (function overloading)
\nTypeScript permite declarar varias firmas para una misma función. Esto es útil cuando tienes combinaciones específicas de parámetros:
\n// Declaramos las sobrecargas\nfunction getData(url: string): Promise<any>;\nfunction getData(url: string, timeout: number): Promise<any>;\nfunction getData(url: string, timeout: number, retries: number): Promise<any>;\n\n// Implementación (debe ser la más general)\nfunction getData(\n url: string,\n timeout?: number,\n retries?: number\n): Promise<any> {\n // lógica\n return fetch(url, { signal: AbortSignal.timeout(timeout ?? 5000) });\n}\n\n// Ahora podemos llamar así:\ngetData('/api/users'); // timeout = 5000 (por defecto)\ngetData('/api/users', 3000); // timeout = 3000\n// Pero así no se puede (omitir timeout no está permitido sin sobrecarga):\n// getData('/api/users', undefined, 2); // malo\n\nDesventaja: las sobrecargas no resuelven el problema de omitir parámetros intermedios. Solo proporcionan combinaciones estrictas. Para una transmisión flexible, es mejor usar la primera forma.
\n\nForma 3: Parámetros Rest + desestructuración (exótica)
\nSi quieres mantener la posicionalidad pero añadir flexibilidad, puedes usar tuplas con rest:
\ntype OptionalParams = [number, string?, boolean?];\n\nfunction processData(\n required: string,\n ...opt