TypeScript 类型推断问题
TypeScript type inference issue
我将 TypeScript 与 MongoDB node.js 驱动程序一起使用。请注意,这不是 Mongo 问题,它只是我遇到的这个问题的特定用例。
几乎每个 Mongo 调用都使用 (arg1, arg2, ..., argn, callback)
的模式,其中 callback
是一个接受 (err, res)
的函数。但是,我想使用承诺。我试图通过编写辅助包装函数来简化我的用法,如下所示:
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
的类型?
Typescript 能够推断出一些通用函数的类型,但它有一些限制。
由于 generic section of the handbook 中没有任何信息,我决定进行一些测试,看看它在哪里出现故障。
- 一个带 1 个参数的简单函数。
function genericFunction<T>(value: T): T {
return value;
}
// type of val is Window
let val = genericFunction(window);
这有效,无需手动指定 T 的类型。
- 具有 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 的类型。
- 接收回调和值的函数。
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 的类型。
- 一个接收回调和值但 returns 承诺的函数。
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 的类型。
- 一个函数只接收一个从T到T的函数
function onlyCallback<T>(action: () => T) : T {
return action();
}
// type of val is string
let val = onlyCallback(()=> "abc");
这有效,无需手动指定 T 的类型。
- 一个接收函数的函数,从无到有returns一个承诺。
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 的类型。
- 一个接收函数的函数。问题中的案例。
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 的类型。
我错了,这在 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;
}
}
我将 TypeScript 与 MongoDB node.js 驱动程序一起使用。请注意,这不是 Mongo 问题,它只是我遇到的这个问题的特定用例。
几乎每个 Mongo 调用都使用 (arg1, arg2, ..., argn, callback)
的模式,其中 callback
是一个接受 (err, res)
的函数。但是,我想使用承诺。我试图通过编写辅助包装函数来简化我的用法,如下所示:
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
的类型?
Typescript 能够推断出一些通用函数的类型,但它有一些限制。
由于 generic section of the handbook 中没有任何信息,我决定进行一些测试,看看它在哪里出现故障。
- 一个带 1 个参数的简单函数。
function genericFunction<T>(value: T): T {
return value;
}
// type of val is Window
let val = genericFunction(window);
这有效,无需手动指定 T 的类型。
- 具有 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 的类型。
- 接收回调和值的函数。
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 的类型。
- 一个接收回调和值但 returns 承诺的函数。
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 的类型。
- 一个函数只接收一个从T到T的函数
function onlyCallback<T>(action: () => T) : T {
return action();
}
// type of val is string
let val = onlyCallback(()=> "abc");
这有效,无需手动指定 T 的类型。
- 一个接收函数的函数,从无到有returns一个承诺。
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 的类型。
- 一个接收函数的函数。问题中的案例。
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 的类型。
我错了,这在 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;
}
}