TypeScript类型推断问题

时间:2015-12-30 14:26:23

标签: typescript type-inference type-safety

我正在将TypeScript与MongoDB node.js驱动程序一起使用。注意,这不是一个Mongo问题,它只是我遇到的这个问题的特定用例。

几乎每个Mongo调用都使用(arg1, arg2, ..., argn, callback)模式,其中callback是一个接受(err, res)的函数。但是,我想使用promises。我正在尝试通过编写辅助包装器函数来简化我的使用,如下所示:

function mongoWrap<T>(action: (callback: (err: Error, res: T) => void) => void) : q.IPromise<T>
{
    var promise = q.Promise<T>((resolve, reject) => {
       action((err, res) => {
           if (err) reject(err);
           else resolve(res);
       });
    });

    return promise;
}

这很好用,除了由于某种原因编译器无法推断出T的类型。我必须拨打电话,如:

var dbPromise = mongoWrap<mongodb.Db>(cb => mongoClient.connect("mongodb://localhost:27017/database", cb));

如果我省略<mongodb.Db>部分,结果为Promise<{}>,我失去了类型安全性。但是,编译器清楚地知道Mongo调用的callback参数是(err: Error, db: Db) => void

如何让编译器正确推断出T的类型?

2 个答案:

答案 0 :(得分:3)

Typescript能够推断出某些泛型函数的类型,但它有一些局限性。

由于generic section of the handbook中没有任何信息,我决定进行一些测试,看看它在哪里发生故障。

  1. 一个带1个参数的简单函数。
  2. function genericFunction<T>(value: T): T {
        return value;
    }
    
    // type of val is Window
    let val = genericFunction(window); 
    

    这很有效,无需手动指定T的类型。

    1. 具有2个通用参数的函数。
    2. function genericFunction2<T>(value: T, anotherValue: T) : T {
          return value;
      }
      
      // type of val is String
      let val = genericFunction2("b", "5"); 
      
      // compilation error type of T can't be inferred from usage
      let anotherVal = genericFunction2("b", 5); 
      

      这很有效,无需手动指定T的类型。

      1. 接收回调和值的函数。
      2. function callBackAndValue<T>(action: (value: T) => T, value: T): T {
            return action(value);
        }
        
        // type of val is string
        let val = callBackAndValue((value: string) => value + "5", "abc "); 
        

        这很有效,无需手动指定T的类型。

        1. 接收回调和值但返回承诺的函数。
        2. function callBackAndValueWithPromise<T>(action: (value: T) => T, value: T): Promise<T> {
              return new Promise<T>((resolve, reject) => {
                  resolve(action(value));
              });
          }
          
          // type of val is Promise<string>
          let val = callBackAndValueWithPromise((value: string) => value + "5", "abc "); 
          

          这很有效,无需手动指定T的类型。

          1. 仅接收从T到T
          2. 的函数的函数
            function onlyCallback<T>(action: () => T) : T {
                return action();
            }
            
            // type of val is string
            let val = onlyCallback(()=> "abc"); 
            

            这很有效,无需手动指定T的类型。

            1. 从无到接收函数的函数,返回一个promise。
            2. function onlyCallbackWithPromise<T>(action: () => T): Promise<T> {
                  return new Promise<T>((resolve, reject) => { 
                      resolve(action());
                  });
              }
              
              // the type of val is Promise<string>
              let val = onlyCallbackWithPromise(()=> "abc"); 
              

              这是有效的,不需要手动指定T的类型。

              1. 接收带函数的函数的函数。问题中的案例。
              2. function typeFromCallbackOfCallback<T>(action: (callback: (value: T) => void) => void): Promise<T> {
                    return new Promise<T>((resolve, reject) => {
                        action((value) => {
                            resolve(value);
                        });
                    });
                }
                
                // here the compiler fails to infer the type cb should take as a parameter and it seems to default to object({})
                // type of Val is Promise<{}>
                let val = typeFromCallbackOfCallback(cb => cb("abc")); 
                

                这不再有效,需要手动指定类型。

                由于目前编译器有限,我猜你不得不为这种情况指定类型。这是手册中给出的解决方案以及类型推断失败的情况。

                添加T类型的另一个参数会修复它,但它并不适合您的情况。

                function lastOne<T>(action: (callback: (value: T) => void) => void, b: T): Promise<T> {
                    return new Promise<T>((resolve, reject) => {
                        action((value) => {
                            resolve(value);
                        });
                    });
                }
                
                // type of var is Promise<string>
                let var = lastOne(cb => cb("abc"), "a");
                

                这是有效的,不需要手动指定T的类型。

答案 1 :(得分:1)

我错了,这在C#中也不起作用。以下是用C#编写的TypeScript中相同代码的示例,但不起作用:

using System.IO;
using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Console.WriteLine("Hello, World!");
        var task = Wrapper(cb => FakeMongoFunctionWithCallback(5, cb));
    }

    static void FakeMongoFunctionWithCallback(int arg, Action<Exception, int> callback)
    {
        // just pass through the arg into the callback, pretending we went to a db and came back with that result
        callback(null, arg);
    }

    static Task<T> Wrapper<T>(Action<Action<Exception, T>> action) {
        var tcs = new TaskCompletionSource<T>();
        action((err, res) => {
            if (err != null) tcs.SetException(err);
            else tcs.SetResult(res);
        });

        return tcs.Task;
    } 
}