Firmas de sobrecarga y firma de implementación
La sobrecarga de funciones en TypeScript permite definir varias firmas para una misma función. Cada firma describe una forma válida de invocar la función con tipos o cantidades de parámetros diferentes. Después de las firmas de sobrecarga se escribe la firma de implementación, que contiene la lógica real y debe ser compatible con todas las firmas anteriores.

function formatear(valor: string): string
function formatear(valor: number): string
function formatear(valor: string | number): string {
if (typeof valor === "string") {
return valor.toUpperCase()
}
return valor.toFixed(2)
}
console.log(formatear("hola")) // "HOLA"
console.log(formatear(3.14159)) // "3.14"
La firma de implementación no es visible para el código externo. Solo las firmas de sobrecarga determinan como se puede invocar la función. Si la firma de implementación no coincide con ninguna firma de sobrecarga, TypeScript genera un error.
La firma de implementación debe cubrir todas las sobrecargas
La firma de implementación utiliza tipos union o parámetros opcionales para manejar todos los casos declarados en las sobrecargas:
function convertir(valor: string): number
function convertir(valor: number): string
function convertir(valor: string | number): string | number {
if (typeof valor === "string") {
const resultado = parseFloat(valor)
if (isNaN(resultado)) {
throw new Error(`No se puede convertir "${valor}" a número`)
}
return resultado
}
return valor.toString()
}
const num = convertir("42") // TypeScript sabe que retorna number
const str = convertir(42) // TypeScript sabe que retorna string
console.log(num) // 42
console.log(str) // "42"
La diferencia clave con una función que simplemente acepta string | number es que las sobrecargas permiten que TypeScript asocie el tipo de retorno con el tipo de entrada. Sin sobrecargas, el tipo de retorno sería siempre string | number.
Sobrecargas con distinto número de parámetros
Las sobrecargas también permiten que una función acepte distintas cantidades de argumentos:
function crearFecha(timestamp: number): Date
function crearFecha(anio: number, mes: number, dia: number): Date
function crearFecha(anioOTimestamp: number, mes?: number, dia?: number): Date {
if (mes !== undefined && dia !== undefined) {
return new Date(anioOTimestamp, mes - 1, dia)
}
return new Date(anioOTimestamp)
}
const fecha1 = crearFecha(1700000000000)
console.log(fecha1.toISOString())
const fecha2 = crearFecha(2025, 6, 15)
console.log(fecha2.toISOString())
// Error: No overload expects 2 arguments
// crearFecha(2025, 6)
La última linea muestra un aspecto importante: aunque la firma de implementación acepta dos argumentos (gracias a los parámetros opcionales), las firmas de sobrecarga solo permiten invocar la función con uno o tres argumentos.
Ejemplos practicos de sobrecarga
Función de busqueda con distintos criterios
Un caso común es una función que busca elementos de formas diferentes según el tipo de parámetro:
interface Usuario {
id: number
nombre: string
email: string
activo: boolean
}
const usuarios: Usuario[] = [
{ id: 1, nombre: "Ana", email: "ana@mail.com", activo: true },
{ id: 2, nombre: "Carlos", email: "carlos@mail.com", activo: false },
{ id: 3, nombre: "Elena", email: "elena@mail.com", activo: true }
]
function buscarUsuario(id: number): Usuario | undefined
function buscarUsuario(email: string): Usuario | undefined
function buscarUsuario(criterio: number | string): Usuario | undefined {
if (typeof criterio === "number") {
return usuarios.find((u) => u.id === criterio)
}
return usuarios.find((u) => u.email === criterio)
}
const porId = buscarUsuario(1)
console.log(porId?.nombre) // "Ana"
const porEmail = buscarUsuario("elena@mail.com")
console.log(porEmail?.nombre) // "Elena"
Función de formato con opciones variables
function formatearFecha(fecha: Date): string
function formatearFecha(fecha: Date, formato: string): string
function formatearFecha(fecha: Date, formato?: string): string {
const dia = fecha.getDate().toString().padStart(2, "0")
const mes = (fecha.getMonth() + 1).toString().padStart(2, "0")
const anio = fecha.getFullYear()
if (formato === "corto") {
return `${dia}/${mes}`
}
if (formato === "iso") {
return fecha.toISOString().split("T")[0]
}
return `${dia}/${mes}/${anio}`
}
const hoy = new Date(2025, 5, 15)
console.log(formatearFecha(hoy)) // "15/06/2025"
console.log(formatearFecha(hoy, "corto")) // "15/06"
console.log(formatearFecha(hoy, "iso")) // "2025-06-15"
Manejador de eventos con distintas firmas
type EventoClick = { tipo: "click"; x: number; y: number }
type EventoTecla = { tipo: "tecla"; código: string }
function manejarEvento(evento: EventoClick): string
function manejarEvento(evento: EventoTecla): string
function manejarEvento(evento: EventoClick | EventoTecla): string {
if (evento.tipo === "click") {
return `Click en (${evento.x}, ${evento.y})`
}
return `Tecla pulsada: ${evento.código}`
}
console.log(manejarEvento({ tipo: "click", x: 100, y: 200 }))
// "Click en (100, 200)"
console.log(manejarEvento({ tipo: "tecla", código: "Enter" }))
// "Tecla pulsada: Enter"
Sobrecargas frente a tipos union
No siempre es necesario usar sobrecargas. TypeScript recomienda preferir parámetros con tipos union cuando las sobrecargas comparten el mismo tipo de retorno o cuando la lógica no requiere discriminar entre tipos de entrada.
Caso donde union es mejor
Cuando todas las sobrecargas tienen el mismo número de parámetros y el mismo tipo de retorno, una union es más simple:
// Con sobrecargas (innecesariamente complejo)
function obtenerLongitudOverload(valor: string): number
function obtenerLongitudOverload(valor: number[]): number
function obtenerLongitudOverload(valor: string | number[]): number {
return valor.length
}
// Con union (mas simple y equivalente)
function obtenerLongitud(valor: string | number[]): number {
return valor.length
}
console.log(obtenerLongitud("TypeScript")) // 10
console.log(obtenerLongitud([1, 2, 3])) // 3
La versión con union permite además pasar valores cuyo tipo sea
string | number[], algo que las sobrecargas no permiten porque TypeScript solo puede resolver una sobrecarga a la vez.
Caso donde sobrecargas son necesarias
Las sobrecargas aportan valor cuando el tipo de retorno depende del tipo de entrada:
// Sin sobrecargas: el retorno siempre es string | number
function procesarSinSobrecarga(valor: string | number): string | number {
if (typeof valor === "string") {
return valor.length
}
return valor.toString()
}
const r1 = procesarSinSobrecarga("hola") // tipo: string | number
// Con sobrecargas: TypeScript conoce el tipo exacto de retorno
function procesar(valor: string): number
function procesar(valor: number): string
function procesar(valor: string | number): string | number {
if (typeof valor === "string") {
return valor.length
}
return valor.toString()
}
const r2 = procesar("hola") // tipo: number
const r3 = procesar(42) // tipo: string
console.log(r2) // 4
console.log(r3) // "42"
Tabla de decisión
La siguiente lógica ayuda a decidir entre sobrecargas y tipos union:
// Usa UNION cuando:
// - Mismo tipo de retorno para todos los casos
// - Mismo número de parámetros
// - No necesitas discriminar el retorno según la entrada
function imprimir(valor: string | number | boolean): void {
console.log(String(valor))
}
// Usa SOBRECARGAS cuando:
// - El tipo de retorno cambia según el tipo de entrada
// - El número de parámetros varia entre invocaciones
// - Necesitas que TypeScript infiera un tipo de retorno específico
function parsear(valor: string): number
function parsear(valor: string, radix: number): number
function parsear(valor: number): string
function parsear(valor: string | number, radix?: number): string | number {
if (typeof valor === "string") {
return parseInt(valor, radix ?? 10)
}
return valor.toString()
}
const n = parsear("FF", 16) // tipo: number
const s = parsear(255) // tipo: string
console.log(n) // 255
console.log(s) // "255"
Buenas prácticas con sobrecargas
Ordenar las firmas de la más específica a la más general
TypeScript evalua las firmas de sobrecarga en orden. Las firmas más específicas deben ir primero para evitar que una firma general capture llamadas que deberian resolverse con una firma específica:
function responder(código: 200): { exito: true; datos: string }
function responder(código: 404): { exito: false; mensaje: string }
function responder(código: number): { exito: boolean }
function responder(código: number): { exito: boolean; datos?: string; mensaje?: string } {
if (código === 200) {
return { exito: true, datos: "OK" }
}
if (código === 404) {
return { exito: false, mensaje: "No encontrado" }
}
return { exito: código >= 200 && código < 300 }
}
const ok = responder(200) // tipo: { exito: true; datos: string }
const noEncontrado = responder(404) // tipo: { exito: false; mensaje: string }
const otro = responder(500) // tipo: { exito: boolean }
console.log(ok.datos) // "OK"
console.log(noEncontrado.mensaje) // "No encontrado"
console.log(otro.exito) // false
Mantener al menos dos firmas de sobrecarga
Una sola firma de sobrecarga no tiene sentido práctico. TypeScript requiere al menos dos firmas antes de la implementación:
// Correcto: dos firmas + implementación
function normalizar(valor: string): string
function normalizar(valor: string[]): string[]
function normalizar(valor: string | string[]): string | string[] {
if (Array.isArray(valor)) {
return valor.map((v) => v.trim().toLowerCase())
}
return valor.trim().toLowerCase()
}
console.log(normalizar(" HOLA ")) // "hola"
console.log(normalizar([" UNO ", " DOS "])) // ["uno", "dos"]
Validar la implementación exhaustivamente
La firma de implementación debe manejar todos los casos declarados en las sobrecargas. Usar comprobaciones de tipo dentro de la implementación asegura que cada rama se ejecute correctamente:
function transformar(valor: string, tipo: "mayusculas"): string
function transformar(valor: string, tipo: "longitud"): number
function transformar(valor: string, tipo: "palabras"): string[]
function transformar(valor: string, tipo: "mayusculas" | "longitud" | "palabras"): string | number | string[] {
switch (tipo) {
case "mayusculas":
return valor.toUpperCase()
case "longitud":
return valor.length
case "palabras":
return valor.split(" ")
}
}
const mayus = transformar("hola mundo", "mayusculas") // tipo: string
const largo = transformar("hola mundo", "longitud") // tipo: number
const partes = transformar("hola mundo", "palabras") // tipo: string[]
console.log(mayus) // "HOLA MUNDO"
console.log(largo) // 10
console.log(partes) // ["hola", "mundo"]
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, TypeScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de TypeScript
Explora más contenido relacionado con TypeScript y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender la diferencia entre firmas de sobrecarga y firma de implementación. Crear funciones sobrecargadas con distintos tipos y cantidades de parámetros. Decidir cuando usar sobrecargas frente a tipos union. Aplicar buenas prácticas en funciones sobrecargadas.