Menu

Sobrecarga de funciones en TypeScript: overload signatures

Las sobrecargas de funciones de TypeScript permiten que una función tenga varias firmas de llamada, cada una con su propio tipo de retorno. Aprende el patrón de firmas de sobrecarga más implementación, las reglas que comprueba el compilador, cuándo es mejor un parámetro unión y las sobrecargas en clases.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

La sobrecarga de funciones en TypeScript consiste en escribir varias firmas de llamada para una función, seguidas de una única implementación. Cada firma puede combinar distintos tipos de parámetros con un tipo de retorno distinto, y quien llama obtiene el preciso.

Sin las sobrecargas, parse devolvería number | number[] en todas las llamadas, y one + 1 sería un error hasta que estrecharas tú mismo el resultado.

Firmas de sobrecarga y la implementación

Una función sobrecargada tiene dos partes:

  1. Firmas de sobrecarga: declaraciones sin cuerpo, una por cada forma de llamada admitida. Son las únicas firmas que puede usar quien llama.
  2. La firma de implementación: la última declaración, la que tiene cuerpo. Sus parámetros deben aceptar todo lo que aceptan las sobrecargas, y su tipo de retorno debe cubrir el tipo de retorno de cada sobrecarga. Desde fuera es invisible.

Los tipos solo existen en tiempo de compilación, así que en tiempo de ejecución hay una sola función de JavaScript. La implementación tiene que inspeccionar sus argumentos (typeof, Array.isArray, arguments.length...) para decidir qué hacer. El compilador comprueba que las sobrecargas y la implementación concuerdan:

function format(value: string): string;
function format(value: number): number {
  return value;
}
// error TS2394: This overload signature is not compatible with its implementation signature.

La solución es ampliar la implementación: function format(value: string | number): string | number.

La firma de implementación no se puede llamar

Esta es la regla que más sorprende. Una llamada tiene que encajar por sí sola con una de las firmas de sobrecarga; TypeScript no las combina.

El compilador muestra:

index.ts(12,19): error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'string | string[]' is not assignable to parameter of type 'string[]'.
      Type 'string' is not assignable to type 'string[]'.

La implementación acepta string | string[], pero quien llama no la ve. Añade una tercera sobrecarga que reciba la unión y devuelva la unión, y la llamada compila e imprime [ 1, 2 ]:

function parse(input: string): number;
function parse(input: string[]): number[];
function parse(input: string | string[]): number | number[];
function parse(input: string | string[]): number | number[] {
  return Array.isArray(input) ? input.map(Number) : Number(input);
}

Distinto número de parámetros

Las sobrecargas también describen llamadas con distinta aridad. Aquí se puede construir una fecha a partir de un timestamp o de año, mes y día, pero no a partir de dos números:

Una sola firma con dos parámetros opcionales aceptaría makeDate(2024, 3) y construiría sin avisar una fecha equivocada. Las sobrecargas lo convierten en un error de compilación (TS2575).

El orden importa

TypeScript prueba las sobrecargas de arriba abajo y elige la primera que encaja. Pon primero las firmas más específicas. Una sobrecarga amplia al principio de la lista se queda con las llamadas pensadas para las que vienen después:

function describe(value: unknown): string;   // matches everything
function describe(value: string): "text";    // never chosen
function describe(value: unknown): string {
  return typeof value === "string" ? "text" : "other";
}

const d = describe("hi"); // d: string, not "text"

Intercambia las dos primeras firmas y describe("hi") pasa a tener el tipo "text".

¿Sobrecargas o un parámetro unión?

Las sobrecargas compensan sus líneas de más cuando el tipo de retorno depende de los tipos de los argumentos. Cuando no es así, una sola firma con un parámetro unión es más corta, más fácil de leer y acepta argumentos unión que las sobrecargas rechazarían.

UsaCuándo
Un parámetro uniónEl mismo tipo de retorno para cualquier entrada
Parámetros opcionalesLas formas de llamada solo se diferencian en argumentos finales que se pueden omitir libremente
SobrecargasEl tipo de retorno cambia según los argumentos, o hay que rechazar algunas combinaciones de argumentos
Un genéricoEl tipo de retorno se construye a partir del tipo del argumento, como identity<T>(x: T): T

Un genérico con un conditional type puede expresar algunos conjuntos de sobrecargas como una sola firma, pero para dos o tres casos las sobrecargas suelen leerse mejor.

Métodos y constructores sobrecargados

Los métodos usan el mismo patrón dentro de una clase: firmas de sobrecarga y después el método con cuerpo. Los constructores se pueden sobrecargar igual.

Las interfaces y los tipos objeto también pueden declarar sobrecargas, como varias firmas de llamada o varias firmas de método con el mismo nombre. Muchas funciones integradas se declaran así: pasa el cursor por reduce sobre un array en un editor y verás "+2 overloads".

Preguntas frecuentes

¿TypeScript admite la sobrecarga de funciones?

Sí, a nivel de tipos. Escribes varias firmas de sobrecarga (declaraciones sin cuerpo) seguidas de una única implementación. Quien llama solo ve las firmas de sobrecarga. En tiempo de ejecución sigue habiendo una sola función de JavaScript, así que la implementación comprueba ella misma los argumentos y maneja cada caso.

¿Qué significa "No overload matches this call"?

Es el error TS2769: los argumentos no encajan en ninguna de las firmas de sobrecarga. La firma de implementación no cuenta, así que una llamada con un argumento unión como string | string[] falla aunque la implementación lo acepte. Añade una sobrecarga que reciba la unión, o sustituye las sobrecargas por una sola firma.

¿Cuándo uso sobrecargas en lugar de un tipo unión?

Usa sobrecargas cuando el tipo de retorno depende de qué tipos de argumento se pasan, por ejemplo si entra string sale number, pero si entra string[] sale number[]. Cuando el tipo de retorno es el mismo para cualquier entrada, una sola firma con un parámetro unión es más sencilla y además acepta argumentos unión.

¿Se pueden sobrecargar funciones flecha en TypeScript?

No con la sintaxis de declaración de sobrecargas, que solo funciona con declaraciones function y métodos. Puedes dar a una variable un tipo sobrecargado con varias firmas de llamada, type Parse = { (s: string): number; (s: string[]): number[] }, pero asignarle una función flecha suele requerir una aserción de tipo, así que una declaración function es la opción más limpia.

¿Por qué mi firma de sobrecarga no es compatible con su firma de implementación?

El error TS2394 significa que una sobrecarga acepta o devuelve algo que la implementación no acepta o no devuelve. Los parámetros de la implementación deben aceptar los de todas las sobrecargas, y su tipo de retorno debe ser compatible con el de cada sobrecarga. Ampliar la implementación (a menudo a una unión) lo arregla.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR