TypeScript函数重载


242

TypeScript语言规范的6.3节讨论了函数重载,并提供了有关如何实现此功能的具体示例。但是,如果我尝试这样的事情:

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";
     }        

}

我收到一个编译器错误,指示重复的标识符,即使函数参数的类型不同。即使我向第二个createFeatureLayer函数添加了一个附加参数,我仍然会遇到编译器错误。请给我个主意。


Answers:


189

这可能是因为,当两个函数都编译为JavaScript时,它们的签名是完全相同的。由于JavaScript没有类型,我们最终创建了两个带有相同数量参数的函数。因此,TypeScript限制我们创建此类函数。

TypeScript支持基于参数数量的重载,但是如果将其与OO语言进行比较,则遵循的步骤会有些不同。在回答另一个SO问题时,有人用一个很好的例子来解释它:方法重载?

基本上,我们正在做的是,我们仅创建一个函数和许多声明,以使TypeScript不会产生编译错误。将此代码编译为JavaScript时,仅可见具体功能。由于可以通过传递多个参数来调用JavaScript函数,因此它可以正常工作。


50
可以对语言进行修改以支持这一点。从理论上讲,可以生成由编译的TypeScript分开命名和调用的函数实现(例如createFeatureLayer_1和createFeatureLayer_2),然后createFeatureLayer可以根据与香草JavaScript互操作的参数内容确定要调用的函数实现。
Thomas S. Trias 2014年

8
您说的话就好像只能基于参数的数量在TypeScript中进行重载,而也可以根据类型进行重载,如Steve Fenton的答案所示。
Matthijs Wessels 2014年

9
这真是la脚。TypeScript实际上应该生成“元函数”,该函数根据传递的内容适当地选择唯一命名的实现。现在情况如何,您可以通过编译器,但是对类型嗅探的实现可能不正确。
以西结Victor

5
如果有一种在运行时检查类型的可靠方法,@ EzekielVictor TypeScript会执行此操作。
索恩

3
这甚至更复杂,可以用JavaScript的类型来实现,但是TS特定的概念(例如接口,types,枚举,泛型等)在运行时会丢失。这就是为什么你做不到的原因someObject instanceof ISomeInterfaceDefinedInTypeScript
Morgan Touverey Quilling

209

当您在TypeScript中重载时,只有一个实现带有多个签名。

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

TypeScript仅将三个重载识别为方法调用的可能签名,而不是实际实现。

在您的情况下,我个人会使用两种名称不同的方法,因为参数没有足够的通用性,这使得方法主体可能需要大量的“如果”来决定要做什么。

TypeScript 1.4

从TypeScript 1.4开始,通常可以使用联合类型来消除对重载的需求。可以使用以下示例更好地表达上面的示例:

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

的类型a是“ string或者number”。


好答案。我只想强调一点,当由于以下原因而试图重载时,这可能无济于事:我想拥有一个实例,在该实例中,使用相同的构造函数,我可以传递定义所有预期属性的对象,一个实例,传递个人参数: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS

在一般情况下,我宁愿使用一个工厂方法来创建我的对象的每个方法-有没有必要的分支,如果你打电话Foo.fromObject(obj)Foo.fromJson(str)等。
芬顿

但这假设人们总是将它们的参数作为一个对象或单个字符串传递,如我先前的评论所强调的,如果我想让它们分别传递怎么办? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) 我还注意到您在FooClass中有不同的方法,fromObject和fromJson?
Hlawuleka MAS

1
如果您将这种差异归结到源头,通常会发现没有必要。例如,无论如何您都必须键入myNum或输入myObj,所以为什么不使用单独的方法并使所有内容清晰/避免不必要的分支逻辑。
芬顿

2
请注意,如果您要基于参数使用不同的返回类型,则使用联合类型可能会出现问题。如果返回类型始终与参数类型之一匹配,则可以使用泛型来解决,但在其他情况下,重载是最佳解决方案。
约翰·蒙哥马利

45

您可以通过将函数声明为具有多个调用签名的类型来声明重载函数:

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

然后是以下内容:

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

函数的实际定义必须是单数,并在内部对其参数执行适当的分派。

例如,使用一个类(可以实现IFoo,但不一定要实现):

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;
    }
}

这里有趣的是,any表单被更具体地键入的替代隐藏

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

1

一般而言,什么是函数重载?

函数重载或方法重载是能够创建多个功能的的相同名称不同的实施方式维基百科


什么是JS中的函数重载?

在JS中无法使用此功能-如果有多个声明,则采用最后定义的功能:

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

...在TS中?

重载是一个编译时结构,对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

如果使用上述代码(比JS安全),则会触发重复的实现错误。TS按自上而下的顺序选择第一个配件重载,因此将重载从最具体到最广泛分类。


TS中的方法重载:一个更复杂的示例

重载的类方法类型可以类似于函数重载的方式使用:

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

由于函数实现与所有重载签名(由编译器强制执行)兼容,因此可能存在截然不同的重载。

更多信息:


0

作为对其他人的提醒,我观察到至少从WebPack为Angular 2编译的TypeScript可以看出,您悄悄地获得了overWRITTEN而不是overLOADED方法。

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

致电:

myComponent.method()

似乎用参数执行该方法,而无提示地忽略了无参数版本,并输出:

with arg

2
您不能为重载声明单独的主体,而只能声明不同的签名。
adharris

5
我不确定您使用的是哪个版本的TypeScript编译器,但是当前版本会发出Duplicate function implementation类似代码的警告。
Royston Shufflebotham

0

打字稿中的函数重载:

根据Wikipedia,(以及许多编程书籍),方法/函数重载的定义如下:

在某些编程语言中,函数重载或方法重载是使用不同的实现创建具有相同名称的多个函数的能力。对重载函数的调用将针对该调用的上下文运行该函数的特定实现,从而允许一个函数调用根据上下文执行不同的任务。

在打字稿中,不能有根据参数的数量和类型调用的同一函数的不同实现。这是因为将TS编译为JS时,JS中的函数具有以下特征:

  • JavaScript函数定义未为其参数指定数据类型
  • JavaScript函数在调用时不检查参数数量

因此,从严格的意义上讲,可以说不存在TS函数重载。但是,您可以在TS代码中做一些可以完美模仿函数重载的事情。

这是一个例子:

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;
  }
}

TS文档称此方法重载,而我们基本上所做的是向TS编译器提供多个方法签名(可能的参数和类型的描述)。现在,TS可以确定在编译期间我们是否正确调用了函数,如果我们错误地调用了函数,则会给我们一个错误。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.