TypeScript type inference issue

后端 未结 2 526
一生所求
一生所求 2021-01-13 08:10

I\'m using TypeScript with the MongoDB node.js driver. Note, this is not a Mongo question, its just the particular use case of this issue I\'m having.

Pretty much ev

相关标签:
2条回答
  • 2021-01-13 08:13

    I was wrong, this doesn't work in C# either. Here's the example of the same code from TypeScript, written in C#, that does not work:

    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;
        } 
    }
    
    0 讨论(0)
  • 2021-01-13 08:32

    Typescript is able to infer the type of some generic functions but it has some limitations.

    Since there isn't any information in the generic section of the handbook I decided to make some tests and see where it breaks down.

    1. a simple function that takes 1 parameter.
    function genericFunction<T>(value: T): T {
        return value;
    }
    
    // type of val is Window
    let val = genericFunction(window); 
    

    This works, there's no need to specify the type of T manually.

    1. a function with 2 generic parameters.
    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); 
    

    This works, there's no need to specify the type of T manually.

    1. a function that receives a callback and a value.
    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 "); 
    

    This works, there's no need to specify the type of T manually.

    1. a function that receives a callback and a value but returns a promise.
    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 "); 
    

    This works, there's no need to specify the type of T manually.

    1. a function that receives only a function from T to T
    function onlyCallback<T>(action: () => T) : T {
        return action();
    }
    
    // type of val is string
    let val = onlyCallback(()=> "abc"); 
    

    This works, there's no need to specify the type of T manually.

    1. A function that receives a function from nothing to T that returns a promise.
    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"); 
    

    This works, there's no need to specify the type of T manually.

    1. A function that receives the a function that takes a function. The case in the question.
    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")); 
    

    This no longer works and needs the type to be specified manually.

    Since the compiler is limited at the moment I guess you are stuck having to specify the type for this case. That is the solution given in the handbook as well for the cases when type inference fails.

    Adding another parameter of type T fixes it, but It doesn't quite match your case.

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

    This works, there's no need to specify the type of T manually.

    0 讨论(0)
提交回复
热议问题