在打字稿中实现函数重载

Posted

技术标签:

【中文标题】在打字稿中实现函数重载【英文标题】:Implement function overloading in typescript 【发布时间】:2019-01-24 09:14:43 【问题描述】:

我知道 typescript 和 javascript 不支持函数重载。

而且我正在研究一种绕路方法来像这个函数重载一样工作。

我的情况如下。

第一:

最后一个和第一个参数是固定的。

public A(arg1:number, arg2:number, argLast:any)



public A(arg1:number, arg2:number, arg3:Array<any>, argLast:any)


第二:

有函数是否重载的指示器。

当然,和上面的例子一样,是可以的,但是我必须通过一个新的接口来创建它,所以它不适合我的情况。

我尝试过各种函数重载方法。

我也通过 john resig 博客实现了它。

(https://johnresig.com/blog/javascript-method-overloading/)

下面的代码是我通过上面的博客做的一个例子。

function addMethod (object, name, fn) 
  var old = object[ name ]
  object[ name ] = function () 
    if (fn.length === arguments.length) 
      return fn.apply(this, arguments)
     else if (typeof old === 'function') 
      return old.apply(this, arguments)
    
  


export class Users
  find(...arg: any[])
    Users.prototype['findOVF'](arg)
  

addMethod(Users.prototype, 'findOVF', function () 
  console.log('ARG 0')
)
addMethod(Users.prototype, 'findOVF', function (name:string, age:number) 
  console.log('ARG 2')
)
addMethod(Users.prototype, 'findOVF', function (first_name:string, last_name:string,age:number) 
  console.log('ARG 3')
)

var users = new Users()
users.find() 
users.find('John',19) 
users.find('John', 'Resig', 19) 

当我使用这个方法时,我用参数(... arg)来调用函数。

我已经尝试实现了下面博客的所有方法。

(https://blog.mariusschulz.com/2016/08/18/function-overloads-in-typescript)

public A(arg1:number, arg2:number, argLast:any)



public A(arg1:number, arg2:number, arg3:Array<any>|null, argLast:any)


如果我以这种方式实现函数重载,每次调用 A 函数时都会出错。这不是真正的函数重载。

但是我还没有找到同时实现这两个元素的方法。

我想知道这对于打字稿语法是不可能的,还是有其他可能性。

感谢您看到我笨拙的英语。

【问题讨论】:

我不太清楚什么不起作用。您能否发布一个完整的示例,其中包含您想要工作但无法正常工作或无法按预期工作的代码? 好的。我现在就发帖。但是,写作会很长。希望你能理解。 我试图描述我尝试的方法。我也有一个问题......对不起。 【参考方案1】:

您可以在 typescript 中创建重载,但所有重载都有一个实现,您可以自行区分它们。最简单的解决方案,如果你的方法都只是通过参数的数量来区分是这样的:

class User  
export class Users 
    find(): User;
    find(name: string, age: number): User;
    find(first_name: string, last_name: string, age: number): User
    find(...args: [any?, any?, any?]): User 
        if (args.length == 0) 
            return null as any;
         else if (args.length == 1) 
            let [name, age, _]: [string?, number?, any?] = args;
            console.log(`$name, $age`);
            return null as any;
         else if (args.length == 2)
            let [first_name, last_name, age]: [string?, string?, number?] = args;
            console.log(`$first_name, $last_name, $age`);
            return null as any;
         else 
            throw "Not implemented";
        
    

Playground link

上述解决方案保留了调用站点类型的安全性,您的解决方案没有,因为在您的示例中find 的参数是any[]

您可以通过定义一个函数来使用更自动化的方法,该函数将从函数数组中创建一个重载函数,但是这种额外的复杂性值得您来判断。

type UnionToIntersection<U> =
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

function overloads<TThis>()
    return function<T extends Array<(this: TThis, ...args: any[]) => any>>(...fns: T): UnionToIntersection<T[number]> 
        return function (this: TThis, ...args: any[]) 
            for (var fn of fns) 
                if (fn.length === args.length) 
                    return fn.apply(this, args);
                
            
            throw "Not implemented";
         as any
    


class User  
export class Users 
    member: string = "M"
    find = overloads<Users>()(
        function () 
            console.log('ARG 0');
            this.member // this is Users 
        ,
        function (name: string, age: number) 
            console.log('ARG 2')
        ,
        function (first_name: string, last_name: string, age: number) 
            console.log('ARG 3')
        
    );


var users = new Users()
users.find('John', 19)
users.find('John', 'Resig', 19) 

Playground link

或者将函数分配给原型而不是实例的版本:

export class Users 
    member: string = "M"


let find = overloads<Users>()(
    function () 
        console.log('ARG 0');
        this.member // this is Users 
    ,
    function (name: string, age: number) 
        console.log('ARG 2')
    ,
    function (first_name: string, last_name: string, age: number) 
        console.log('ARG 3')
    
);
export interface Users 
    find: typeof find;

Users.prototype.find = find;

Playground link

【讨论】:

谢谢您的热心回答!但我有一些错误:rest 参数必须是数组类型。而 JSDoc 类型只能在文档 cmets 中使用。 @naiad 你用的是什么版本的打字稿,上面的代码适用于3.0 @naiad 第一个版本适用于任何版本的打字稿,并且是最容易理解的版本,对于简单的重载,我会使用那个版本 我的 IDE 是 Visual Studio 代码,当我编写“tsc -v”命令时:版本 3.0.1 @naiad 为所有示例添加了游乐场链接,它们都可以正常工作,并且应该在您的 IDE 中也可以工作。

以上是关于在打字稿中实现函数重载的主要内容,如果未能解决你的问题,请参考以下文章

Typescript子类函数重载

再次:打字稿函数重载

打字稿中的重载函数类型

带有箭头函数的打字稿重载方法

难以理解的打字稿泛型函数重载

基于可选参数存在而不使用函数重载的打字稿函数返回类型