Sobrecarga de la función TypeScript

244

La sección 6.3 de la especificación del lenguaje TypeScript habla sobre la sobrecarga de funciones y proporciona ejemplos concretos sobre cómo implementar esto. Sin embargo, si intento algo como esto:

export class LayerFactory { 

    constructor (public styleFactory: Symbology.StyleFactory) { }

    createFeatureLayer (userContext : Model.UserContext, mapWrapperObj : MapWrapperBase) : any {           
         throw "not implemented";
    }                 

    createFeatureLayer(layerName : string, style : any) : any {
        throw "not implemented";
     }        

}

Recibo un error del compilador que indica un identificador duplicado a pesar de que los parámetros de función son de diferentes tipos. Incluso si agrego un parámetro adicional a la segunda función createFeatureLayer, sigo obteniendo un error del compilador. Ideas, por favor.

Klaus Nji
fuente
¿Posible duplicado de sobrecarga
BuZZ-dEE

Respuestas:

189

Esto puede deberse a que, cuando ambas funciones se compilan en JavaScript, su firma es totalmente idéntica. Como JavaScript no tiene tipos, terminamos creando dos funciones con la misma cantidad de argumentos. Entonces, TypeScript nos restringe de crear tales funciones.

TypeScript admite la sobrecarga en función del número de parámetros, pero los pasos a seguir son un poco diferentes si los comparamos con los lenguajes OO. En respuesta a otra pregunta de SO, alguien lo explicó con un buen ejemplo: ¿Sobrecarga de método? .

Básicamente, lo que estamos haciendo es crear una sola función y varias declaraciones para que TypeScript no dé errores de compilación. Cuando este código se compila en JavaScript, solo se verá la función concreta. Como se puede invocar una función de JavaScript pasando varios argumentos, simplemente funciona.

S. Ravi Kiran
fuente
50
El lenguaje podría ser modificado para apoyar esto. En teoría, uno podría generar implementaciones de funciones que se nombran aparte y se invocan mediante TypeScript compilado (por ejemplo, createFeatureLayer_1 y createFeatureLayer_2) y createFeatureLayer podría determinar a cuál llamar en función del contenido de los argumentos para la interoperación con JavaScript de vainilla.
Thomas S. Trias
8
Lo expresa como si la sobrecarga en TypeScript solo fuera posible en función del número de parámetros, mientras que la sobrecarga en función del tipo también es posible como se muestra en la respuesta de Steve Fenton.
Matthijs Wessels
99
Esto es un poco cojo; TypeScript realmente debería estar generando la "meta función" que elige la implementación con un nombre único de forma adecuada en función de lo que se pasó. Cómo es ahora hay una grieta en la que podría pasar el compilador, pero su implementación del tipo sniffing podría ser incorrecta.
Ezequiel Victor
55
@EzekielVictor TypeScript lo haría si hubiera una forma confiable de verificar los tipos en tiempo de ejecución.
espina
3
Eso es aún más complicado, es factible con los tipos de JavaScript, pero las nociones específicas de TS como interfaces, types, enumeraciones, genéricos, etc., se pierden en el tiempo de ejecución. Por eso también no puedes hacerlo someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Touverey Quilling
209

Cuando sobrecarga en TypeScript, solo tiene una implementación con múltiples firmas.

class Foo {
    myMethod(a: string);
    myMethod(a: number);
    myMethod(a: number, b: string);
    myMethod(a: any, b?: string) {
        alert(a.toString());
    }
}

TypeScript reconoce solo las tres sobrecargas como posibles firmas para una llamada a método, no la implementación real.

En su caso, personalmente usaría dos métodos con nombres diferentes, ya que no hay suficientes puntos en común en los parámetros, lo que hace que sea probable que el cuerpo del método tenga que tener muchos "si" para decidir qué hacer.

TypeScript 1.4

A partir de TypeScript 1.4, normalmente puede eliminar la necesidad de una sobrecarga utilizando un tipo de unión. El ejemplo anterior se puede expresar mejor usando:

myMethod(a: string | number, b?: string) {
    alert(a.toString());
}

El tipo de aes "cualquiera stringo number".

Fenton
fuente
Gran respuesta. Solo me gustaría destacar que, esto podría no ser útil cuando uno intenta sobrecargar por razones como: Me gustaría tener una instancia, donde usando el mismo constructor, puedo pasar un objeto que defina todas las propiedades esperadas y en la única instancia, pasar parámetros individuales: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS
En general, yo prefiero usar un método de fábrica para mí crear un objeto en cada sentido - no hay necesidad de rama si llamas Foo.fromObject(obj)y Foo.fromJson(str)etcétera.
Fenton
Pero eso postula que uno siempre pasará sus parámetros como un objeto o una sola cadena, ¿qué pasa si quiero que se pasen por separado, como se destacó en mi comentario anterior? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) También noté que tienes diferentes métodos en la FooClase, desde Objeto y desde Json?
Hlawuleka MAS
1
Si sigue esa diferencia hasta la fuente, generalmente encontrará que no es necesario. Por ejemplo, debe escribir myNumo de myObjtodos modos, entonces, ¿por qué no tener métodos separados y aclarar todo / evitar una lógica de ramificación innecesaria?
Fenton
2
Tenga en cuenta que el uso de un tipo de unión puede ser problemático si desea tener diferentes tipos de retorno basados ​​en los parámetros. Eso se puede resolver con genéricos si el tipo de retorno siempre coincide con uno de los tipos de parámetros, pero para otros casos las sobrecargas son la mejor solución.
John Montgomery
45

Puede declarar una función sobrecargada declarando que la función tiene un tipo que tiene múltiples firmas de invocación:

interface IFoo
{
    bar: {
        (s: string): number;
        (n: number): string;
    }
}

Entonces lo siguiente:

var foo1: IFoo = ...;

var n: number = foo1.bar('baz');     // OK
var s: string = foo1.bar(123);       // OK
var a: number[] = foo1.bar([1,2,3]); // ERROR

La definición real de la función debe ser singular y realizar el despacho apropiado internamente en sus argumentos.

Por ejemplo, usando una clase (que podría implementarse IFoo, pero no tiene que hacerlo):

class Foo
{
    public bar(s: string): number;
    public bar(n: number): string;
    public bar(arg: any): any 
    {
        if (typeof(arg) === 'number')
            return arg.toString();
        if (typeof(arg) === 'string')
            return arg.length;
    }
}

Lo interesante aquí es que el anyformulario está oculto por las anulaciones más específicamente escritas.

var foo2: new Foo();

var n: number = foo2.bar('baz');     // OK
var s: string = foo2.bar(123);       // OK
var a: number[] = foo2.bar([1,2,3]); // ERROR
Drew Noakes
fuente
1

¿Qué es la sobrecarga de funciones en general?

La sobrecarga de funciones o la sobrecarga de métodos es la capacidad de crear múltiples funciones del mismo nombre con diferentes implementaciones ( Wikipedia )


¿Qué es la sobrecarga de funciones en JS?

Esta característica no es posible en JS: la última función definida se toma en caso de declaraciones múltiples:

function foo(a1, a2) { return `${a1}, ${a2}` }
function foo(a1) { return `${a1}` } // replaces above `foo` declaration
foo(42, "foo") // "42"

... y en TS?

Las sobrecargas son una construcción en tiempo de compilación sin impacto en el tiempo de ejecución de JS:

function foo(s: string): string // overload #1 of foo
function foo(s: string, n: number): number // overload #2 of foo
function foo(s: string, n?: number): string | number {/* ... */} // foo implementation

Se activa un error de implementación duplicado, si utiliza el código anterior (más seguro que JS). TS elige la primera sobrecarga de ajuste en orden de arriba hacia abajo, por lo que las sobrecargas se ordenan de la más específica a la más amplia.


Método de sobrecarga en TS: un ejemplo más complejo

Los tipos de métodos de clase sobrecargados se pueden usar de manera similar para sobrecargar la función:

class LayerFactory {
    createFeatureLayer(a1: string, a2: number): string
    createFeatureLayer(a1: number, a2: boolean, a3: string): number
    createFeatureLayer(a1: string | number, a2: number | boolean, a3?: string)
        : number | string { /*... your implementation*/ }
}

const fact = new LayerFactory()
fact.createFeatureLayer("foo", 42) // string
fact.createFeatureLayer(3, true, "bar") // number

Las sobrecargas muy diferentes son posibles, ya que la implementación de la función es compatible con todas las firmas de sobrecarga, aplicada por el compilador.

Más información:

ford04
fuente
0

Como aviso a los demás, he observado que, al menos según lo manifestado por TypeScript compilado por WebPack para Angular 2, silenciosamente se ESCRIBE en lugar de métodos sobrecargados.

myComponent {
  method(): { console.info("no args"); },
  method(arg): { console.info("with arg"); }
}

Vocación:

myComponent.method()

parece ejecutar el método con argumentos, ignorando silenciosamente la versión sin argumentos, con salida:

with arg
mtyson
fuente
2
No puede declarar cuerpos separados para sus sobrecargas, solo firmas diferentes.
adharris el
55
No estoy seguro de qué versión del compilador TypeScript está utilizando, pero la versión actual emite una Duplicate function implementationadvertencia para un código como este.
Royston Shufflebotham
0

Sobrecarga de funciones en mecanografiado:

Según Wikipedia, (y muchos libros de programación) la definición de sobrecarga de método / función es la siguiente:

En algunos lenguajes de programación, la sobrecarga de funciones o la sobrecarga de métodos es la capacidad de crear múltiples funciones del mismo nombre con diferentes implementaciones . Las llamadas a una función sobrecargada ejecutarán una implementación específica de esa función adecuada al contexto de la llamada, permitiendo que una llamada de función realice diferentes tareas dependiendo del contexto.

En typecript no podemos tener implementaciones diferentes de la misma función que se llaman de acuerdo con el número y tipo de argumentos. Esto se debe a que cuando TS se compila a JS, las funciones en JS tienen las siguientes características:

  • Las definiciones de funciones de JavaScript no especifican tipos de datos para sus parámetros
  • Las funciones de JavaScript no comprueban el número de argumentos cuando se llama

Por lo tanto, en sentido estricto, se podría argumentar que la sobrecarga de la función TS no existe. Sin embargo, hay cosas que puede hacer dentro de su código TS que pueden imitar perfectamente la sobrecarga de funciones.

Aquí hay un ejemplo:

function add(a: number, b: number, c: number): number;
function add(a: number, b: number): any;
function add(a: string, b: string): any;

function add(a: any, b: any, c?: any): any {
  if (c) {
    return a + c;
  }
  if (typeof a === 'string') {
    return `a is ${a}, b is ${b}`;
  } else {
    return a + b;
  }
}

Los documentos de TS llaman a este método sobrecarga, y lo que básicamente hicimos fue proporcionar firmas de métodos múltiples (descripciones de posibles parámetros y tipos) al compilador de TS. Ahora TS puede descubrir si llamamos a nuestra función correctamente durante el tiempo de compilación y darnos un error si llamamos a la función incorrectamente.

Willem van der Veen
fuente