Tipado de funciones básico

Intermedio
TypeScript
TypeScript
Actualizado: 27/08/2025

Parámetros tipados y valores de retorno

El tipado de funciones en TypeScript nos permite especificar qué tipos de datos pueden recibir nuestras funciones como parámetros y qué tipo de valor van a devolver. Esta característica nos ayuda a detectar errores en tiempo de desarrollo y hace que nuestro código sea más predecible y fácil de mantener.

La sintaxis básica para tipar una función es sencilla: especificamos el tipo después de cada parámetro usando dos puntos, y el tipo de retorno después de los paréntesis de los parámetros.

function nombreFuncion(parametro: tipo): tipoRetorno {
    // código de la función
}

Tipado de parámetros

Los parámetros tipados nos aseguran que solo podemos pasar valores del tipo correcto a nuestras funciones. Veamos algunos ejemplos prácticos:

function saludar(nombre: string): void {
    console.log(`Hola, ${nombre}!`);
}

saludar("Ana"); // ✅ Correcto
// saludar(123); // ❌ Error: no se puede pasar un número

Podemos usar cualquiera de los tipos primitivos que ya conocemos:

function calcularArea(base: number, altura: number): number {
    return base * altura;
}

function esMayorDeEdad(edad: number): boolean {
    return edad >= 18;
}

function mostrarInfo(activo: boolean): string {
    return activo ? "Usuario activo" : "Usuario inactivo";
}

Tipado con arrays

También podemos tipar parámetros que reciben arrays, usando la sintaxis que ya hemos visto:

function sumarNumeros(numeros: number[]): number {
    let suma = 0;
    for (const numero of numeros) {
        suma += numero;
    }
    return suma;
}

function unirTextos(textos: string[]): string {
    return textos.join(" ");
}

Funciones con múltiples parámetros

Cuando una función tiene varios parámetros, cada uno debe tener su tipo especificado:

function crearUsuario(id: number, nombre: string, email: string, activo: boolean): string {
    return `Usuario creado: ${nombre} (${email}) - ID: ${id} - Activo: ${activo}`;
}

// Uso correcto
const mensaje = crearUsuario(1, "Carlos", "carlos@email.com", true);

Tipado del valor de retorno

El tipo de retorno se especifica después de los parénteses de los parámetros. TypeScript puede inferir automáticamente el tipo de retorno en muchos casos, pero es una buena práctica especificarlo explícitamente:

// TypeScript infiere que retorna number
function multiplicar(a: number, b: number) {
    return a * b;
}

// Mejor práctica: especificar el tipo explícitamente
function dividir(a: number, b: number): number {
    return a / b;
}

El tipo void

Cuando una función no retorna ningún valor, usamos el tipo void:

function mostrarMensaje(mensaje: string): void {
    console.log(mensaje);
    // No hay return, o return sin valor
}

function limpiarConsola(): void {
    console.clear();
}

Funciones que retornan objetos

También podemos especificar que una función retorna un objeto con una estructura determinada:

function crearPersona(nombre: string, edad: number): { nombre: string; edad: number; adulto: boolean } {
    return {
        nombre: nombre,
        edad: edad,
        adulto: edad >= 18
    };
}

const persona = crearPersona("Luis", 25);
console.log(persona.nombre); // "Luis"
console.log(persona.adulto); // true

Funciones arrow con tipado

Las funciones arrow siguen la misma sintaxis de tipado:

const calcularDescuento = (precio: number, descuento: number): number => {
    return precio - (precio * descuento / 100);
};

const formatearTexto = (texto: string): string => texto.toUpperCase().trim();

const validarEmail = (email: string): boolean => email.includes("@");

Detección de errores

Una de las principales ventajas del tipado de funciones es que TypeScript nos ayuda a detectar errores comunes:

function calcularImpuesto(precio: number, tasa: number): number {
    return precio * tasa;
}

// ❌ Error: se esperaba number, se recibió string
// calcularImpuesto("100", 0.21);

// ❌ Error: faltan argumentos
// calcularImpuesto(100);

// ❌ Error: demasiados argumentos
// calcularImpuesto(100, 0.21, "extra");

// ✅ Correcto
const impuesto = calcularImpuesto(100, 0.21);

El tipado básico de funciones es fundamental para escribir código TypeScript robusto. Al especificar los tipos de parámetros y valores de retorno, creamos un contrato claro sobre cómo debe usarse cada función, lo que facilita el mantenimiento del código y reduce significativamente los errores en tiempo de ejecución.

Funciones opcionales y parámetros por defecto

En muchas ocasiones necesitamos crear funciones que sean flexibles en cuanto al número de parámetros que pueden recibir. TypeScript nos proporciona dos mecanismos principales para lograr esto: los parámetros opcionales y los parámetros con valores por defecto.

Parámetros opcionales

Los parámetros opcionales se definen añadiendo un signo de interrogación (?) después del nombre del parámetro y antes de los dos puntos que indican el tipo. Esto significa que el parámetro puede estar presente o no cuando llamamos a la función.

function presentarse(nombre: string, apellido?: string): string {
    if (apellido) {
        return `Hola, soy ${nombre} ${apellido}`;
    }
    return `Hola, soy ${nombre}`;
}

// Ambas llamadas son válidas
console.log(presentarse("María")); // "Hola, soy María"
console.log(presentarse("María", "García")); // "Hola, soy María García"

Es importante recordar que los parámetros opcionales siempre deben ir después de los parámetros obligatorios:

// ✅ Correcto: opcional al final
function crearPerfil(nombre: string, edad: number, bio?: string): string {
    let perfil = `${nombre}, ${edad} años`;
    if (bio) {
        perfil += ` - ${bio}`;
    }
    return perfil;
}

// ❌ Incorrecto: no se puede poner opcional antes de obligatorio
// function malEjemplo(nombre?: string, edad: number): string { ... }

Parámetros con valores por defecto

Los parámetros por defecto nos permiten especificar un valor que se usará automáticamente cuando no se proporcione ese parámetro al llamar a la función:

function calcularPrecioFinal(precio: number, descuento: number = 0, impuesto: number = 21): number {
    const precioConDescuento = precio - (precio * descuento / 100);
    return precioConDescuento + (precioConDescuento * impuesto / 100);
}

// Diferentes formas de llamar la función
console.log(calcularPrecioFinal(100)); // precio=100, descuento=0, impuesto=21
console.log(calcularPrecioFinal(100, 10)); // precio=100, descuento=10, impuesto=21
console.log(calcularPrecioFinal(100, 10, 15)); // precio=100, descuento=10, impuesto=15

Inferencia de tipos con valores por defecto

Cuando usamos valores por defecto, TypeScript puede inferir automáticamente el tipo del parámetro basándose en el valor predeterminado:

// TypeScript infiere que mensaje es string y repeticiones es number
function mostrarMensaje(mensaje: string, repeticiones = 1): void {
    for (let i = 0; i < repeticiones; i++) {
        console.log(mensaje);
    }
}

// También podemos ser explícitos con el tipo
function configurarServidor(host: string = "localhost", puerto: number = 3000): string {
    return `Servidor configurado en ${host}:${puerto}`;
}

Combinar parámetros opcionales y por defecto

Podemos combinar ambas técnicas en una misma función, pero debemos tener cuidado con el orden:

function enviarEmail(
    destinatario: string, 
    asunto: string, 
    mensaje?: string, 
    prioridad: string = "normal"
): string {
    let email = `Para: ${destinatario}\nAsunto: ${asunto}\nPrioridad: ${prioridad}`;
    
    if (mensaje) {
        email += `\nMensaje: ${mensaje}`;
    }
    
    return email;
}

// Ejemplos de uso
console.log(enviarEmail("ana@email.com", "Reunión"));
console.log(enviarEmail("carlos@email.com", "Urgente", "Necesito respuesta", "alta"));

Parámetros por defecto con tipos complejos

Los valores por defecto también pueden ser objetos, arrays o valores más complejos:

function crearConfiguracion(
    nombre: string,
    opciones: { tema: string; idioma: string } = { tema: "claro", idioma: "es" }
): string {
    return `Configuración ${nombre}: tema ${opciones.tema}, idioma ${opciones.idioma}`;
}

function procesarLista(
    titulo: string,
    elementos: string[] = [],
    separador: string = ", "
): string {
    if (elementos.length === 0) {
        return `${titulo}: Lista vacía`;
    }
    return `${titulo}: ${elementos.join(separador)}`;
}

Funciones arrow con parámetros opcionales y por defecto

Las funciones arrow siguen exactamente las mismas reglas:

const formatearNombre = (
    nombre: string, 
    apellido?: string, 
    formato: string = "completo"
): string => {
    if (formato === "inicial" && apellido) {
        return `${nombre} ${apellido.charAt(0)}.`;
    }
    return apellido ? `${nombre} ${apellido}` : nombre;
};

const calcularArea = (ancho: number, alto: number = ancho): number => ancho * alto;

Validación con parámetros opcionales

Es importante validar adecuadamente los parámetros opcionales para evitar errores en tiempo de ejecución:

function buscarUsuario(id: number, incluirPerfil?: boolean): string {
    let resultado = `Usuario ID: ${id}`;
    
    // Validación explícita
    if (incluirPerfil === true) {
        resultado += " - Perfil incluido";
    }
    
    return resultado;
}

function procesarTexto(texto: string, longitud?: number): string {
    // Usar valor por defecto si no se proporciona
    const longitudFinal = longitud ?? 100;
    
    return texto.length > longitudFinal 
        ? texto.substring(0, longitudFinal) + "..." 
        : texto;
}

Casos prácticos de uso

Los parámetros opcionales y por defecto son especialmente útiles en escenarios del mundo real:

// Función para crear elementos HTML
function crearBoton(
    texto: string, 
    tipo: string = "button", 
    clase?: string
): string {
    let boton = `<button type="${tipo}"`;
    
    if (clase) {
        boton += ` class="${clase}"`;
    }
    
    boton += `>${texto}</button>`;
    return boton;
}

// Función para conectar a base de datos
function conectarDB(
    host: string,
    puerto: number = 5432,
    usuario: string = "admin",
    timeout?: number
): string {
    let conexion = `Conectando a ${host}:${puerto} como ${usuario}`;
    
    if (timeout) {
        conexion += ` (timeout: ${timeout}ms)`;
    }
    
    return conexion;
}

Los parámetros opcionales y por defecto hacen que nuestras funciones sean más flexibles y fáciles de usar, permitiendo que los usuarios de nuestro código puedan llamarlas con diferentes combinaciones de argumentos según sus necesidades específicas, manteniendo siempre la seguridad de tipos que nos proporciona TypeScript.

Funciones con rest parameters

Los rest parameters nos permiten crear funciones que pueden recibir un número variable de argumentos del mismo tipo. Esta funcionalidad es especialmente útil cuando no sabemos de antemano cuántos valores vamos a recibir, pero sabemos que todos serán del mismo tipo.

Sintaxis básica de rest parameters

La sintaxis de rest parameters utiliza tres puntos (...) seguidos del nombre del parámetro y su tipo como array:

function sumar(...numeros: number[]): number {
    let total = 0;
    for (const numero of numeros) {
        total += numero;
    }
    return total;
}

// Podemos llamarla con cualquier cantidad de números
console.log(sumar(1, 2, 3)); // 6
console.log(sumar(10, 20)); // 30
console.log(sumar(5)); // 5
console.log(sumar()); // 0

El rest parameter siempre se comporta como un array dentro de la función, incluso si no se pasan argumentos (en cuyo caso será un array vacío).

Rest parameters con diferentes tipos

Podemos usar rest parameters con cualquier tipo de datos:

function concatenarTextos(...textos: string[]): string {
    return textos.join(" ");
}

function mostrarBoleanos(...valores: boolean[]): void {
    valores.forEach((valor, indice) => {
        console.log(`Valor ${indice + 1}: ${valor}`);
    });
}

// Ejemplos de uso
console.log(concatenarTextos("Hola", "mundo", "desde", "TypeScript")); 
// "Hola mundo desde TypeScript"

mostrarBoleanos(true, false, true);
// Valor 1: true
// Valor 2: false  
// Valor 3: true

Combinar rest parameters con parámetros normales

Los rest parameters deben ir siempre al final, pero podemos combinarlos con parámetros normales y opcionales:

function crearMensaje(prefijo: string, ...contenidos: string[]): string {
    return `${prefijo}: ${contenidos.join(", ")}`;
}

function calcularPromedio(nombre: string, ...calificaciones: number[]): string {
    if (calificaciones.length === 0) {
        return `${nombre}: Sin calificaciones`;
    }
    
    const promedio = calificaciones.reduce((sum, cal) => sum + cal, 0) / calificaciones.length;
    return `${nombre}: Promedio ${promedio.toFixed(2)}`;
}

// Ejemplos de uso
console.log(crearMensaje("Lista", "manzanas", "peras", "plátanos"));
// "Lista: manzanas, peras, plátanos"

console.log(calcularPromedio("Ana", 8.5, 9.0, 7.5, 8.8));
// "Ana: Promedio 8.45"

Rest parameters con parámetros por defecto

También podemos combinar rest parameters con parámetros que tienen valores por defecto, siempre respetando el orden: parámetros normales, parámetros por defecto, rest parameters:

function configurarServidor(
    nombre: string, 
    puerto: number = 3000, 
    ...middlewares: string[]
): string {
    let config = `Servidor ${nombre} en puerto ${puerto}`;
    
    if (middlewares.length > 0) {
        config += `\nMiddlewares: ${middlewares.join(", ")}`;
    }
    
    return config;
}

// Diferentes formas de llamar la función
console.log(configurarServidor("API"));
console.log(configurarServidor("API", 8080));
console.log(configurarServidor("API", 8080, "cors", "auth", "logger"));

Rest parameters con tipos de objetos

Los rest parameters también pueden recibir arrays de objetos con una estructura específica:

function procesarUsuarios(...usuarios: { nombre: string; edad: number }[]): string {
    if (usuarios.length === 0) {
        return "No hay usuarios para procesar";
    }
    
    const resumen = usuarios.map(usuario => 
        `${usuario.nombre} (${usuario.edad} años)`
    ).join(", ");
    
    return `Procesados ${usuarios.length} usuarios: ${resumen}`;
}

// Uso con objetos
const resultado = procesarUsuarios(
    { nombre: "Carlos", edad: 25 },
    { nombre: "Ana", edad: 30 },
    { nombre: "Luis", edad: 28 }
);
console.log(resultado);
// "Procesados 3 usuarios: Carlos (25 años), Ana (30 años), Luis (28 años)"

Funciones arrow con rest parameters

Las funciones arrow también soportan rest parameters con la misma sintaxis:

const multiplicarTodos = (...numeros: number[]): number => {
    return numeros.reduce((producto, num) => producto * num, 1);
};

const encontrarMaximo = (...valores: number[]): number | null => {
    if (valores.length === 0) return null;
    return Math.max(...valores);
};

const crearLista = (titulo: string, ...elementos: string[]): string => {
    if (elementos.length === 0) {
        return `${titulo}: Lista vacía`;
    }
    return `${titulo}:\n${elementos.map(elem => `- ${elem}`).join("\n")}`;
};

Casos prácticos con rest parameters

Los rest parameters son especialmente útiles en situaciones del mundo real donde necesitamos flexibilidad:

// Sistema de logging
function log(nivel: string, ...mensajes: string[]): void {
    const timestamp = new Date().toISOString();
    const contenido = mensajes.join(" ");
    console.log(`[${timestamp}] ${nivel.toUpperCase()}: ${contenido}`);
}

log("info", "Usuario", "conectado", "correctamente");
log("error", "Falló", "la", "conexión", "a", "la", "base", "de", "datos");

// Validador de formularios
function validarCampos(...campos: { nombre: string; valor: string; requerido: boolean }[]): string[] {
    const errores: string[] = [];
    
    for (const campo of campos) {
        if (campo.requerido && !campo.valor.trim()) {
            errores.push(`El campo ${campo.nombre} es obligatorio`);
        }
    }
    
    return errores;
}

const errores = validarCampos(
    { nombre: "email", valor: "", requerido: true },
    { nombre: "nombre", valor: "Juan", requerido: true },
    { nombre: "telefono", valor: "", requerido: false }
);

Diferencias importantes con arrays normales

Es crucial entender que los rest parameters no son lo mismo que recibir un array como parámetro normal:

// Con rest parameters - llamamos con argumentos separados
function sumarRest(...numeros: number[]): number {
    return numeros.reduce((sum, num) => sum + num, 0);
}

// Con array normal - llamamos con un array
function sumarArray(numeros: number[]): number {
    return numeros.reduce((sum, num) => sum + num, 0);
}

// Formas de llamar cada función
console.log(sumarRest(1, 2, 3, 4)); // ✅ Correcto con rest
console.log(sumarArray([1, 2, 3, 4])); // ✅ Correcto con array normal

// console.log(sumarRest([1, 2, 3, 4])); // ❌ Error con rest
// console.log(sumarArray(1, 2, 3, 4)); // ❌ Error con array normal

Trabajar con el operador spread

Los rest parameters funcionan perfectamente con el operador spread cuando necesitamos pasar arrays existentes:

function combinarArrays(...arrays: number[][]): number[] {
    const resultado: number[] = [];
    for (const array of arrays) {
        resultado.push(...array);
    }
    return resultado;
}

const numeros1 = [1, 2, 3];
const numeros2 = [4, 5, 6];
const numeros3 = [7, 8, 9];

const combinados = combinarArrays(numeros1, numeros2, numeros3);
console.log(combinados); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en TypeScript

Documentación oficial de TypeScript
Alan Sastre - Autor del tutorial

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 cómo tipar parámetros y valores de retorno en funciones TypeScript.
  • Aprender a usar parámetros opcionales y valores por defecto para funciones flexibles.
  • Entender el uso y sintaxis de rest parameters para argumentos variables.
  • Saber tipar funciones que retornan objetos y funciones arrow.
  • Detectar errores comunes mediante el tipado estricto de funciones.