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 every Mongo call uses a pattern of (arg1, arg2, ..., argn, callback)
where callback
is a function that accepts (err, res)
. However, I want to use promises. I'm trying to simplify my usage by writing a helper wrapper function, like so:
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;
}
This works great, except that for some reason the compiler cannot infer the type of T
. I have to make calls such as:
var dbPromise = mongoWrap<mongodb.Db>(cb => mongoClient.connect("mongodb://localhost:27017/database", cb));
If I omit the <mongodb.Db>
part, the result is Promise<{}>
and I lose type safety. However, the compiler clearly knows that the callback
argument to the Mongo call is (err: Error, db: Db) => void
.
What can I do to get the compiler to correctly infer the type of T
?
TypeScript infers types of variables when there is no explicit information available in the form of type annotations. Types are inferred by TypeScript compiler when: Variables are initialized. Default values are set for parameters.
The infer keyword allows types to be extracted from conditions in conditional types. This is often used within Typescript's standard utility types. In the next lesson, we will gain a deep understanding of some of TypeScripts standard conditional utility types.
The best common type algorithm In this case, TypeScript selects the number array type ( number[] ) as the best common type. In this example, TypeScript infers the type for arr to be (RegExp | Date)[] .
It involves analyzing a program and then inferring the different types of some or all expressions in that program so that the programmer does not need to explicitly input and define data types every time variables are used in the program.
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.
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.
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With