I’m writing definitions for es6-promise-pool
to add to DefinitelyTyped, with some tweaks from the discussion at GitHub. The library can take an optional promise
parameter that specifies a class to use for the returned promise (e.g. ES6-Promise
’s polyfill or Bluebird
). Internally, all it cares about is that you can call new Promise(…)
, so this is enough to specify the type of the value:
interface PromiseClass<A, P extends PromiseLike<A>> {
new(callback: (resolve: (value?: A | P) => void, reject: (reason?: any) => void) => void): P;
}
const bar: PromiseClass<number, Bluebird<number>> = Bluebird; // OK
Where things get hairy is in specifying the type of the options:
interface Options<A, P extends PromiseLike<A>, C extends PromiseClass<A, P>> {
promise?: C;
}
function foo<A, P extends PromiseLike<A>, C extends PromiseClass<A, P>>(options: Options<A, P, C>) { /* empty */ }
foo({ promise: Bluebird.resolve(3) });
This gives me an error:
Argument of type '{ promise: Bluebird; }' is not assignable to parameter of type 'Options, PromiseClass>>'. Types of property 'promise' are incompatible. Type 'Bluebird' is not assignable to type 'PromiseClass> | undefined'. Type 'Bluebird' is not assignable to type 'PromiseClass>'. Type 'Bluebird' provides no match for the signature 'new (callback: (resolve: (value?: {} | PromiseLike | undefined) => void, reject: (reason?: any) => void) => void): PromiseLike'.
(Note that the type is inferred as {}
.)
If I manually specify the types:
foo<number, Bluebird<number>, PromiseClass<number, Bluebird<number>>>({ promise: Bluebird.resolve(3) });
I get a similar error:
Argument of type '{ promise: Bluebird; }' is not assignable to parameter of type 'Options, PromiseClass>>'. Types of property 'promise' are incompatible. Type 'Bluebird' is not assignable to type 'PromiseClass> | undefined'. Type 'Bluebird' is not assignable to type 'PromiseClass>'. Type 'Bluebird' provides no match for the signature 'new (callback: (resolve: (value?: number | PromiseLike | undefined) => void, reject: (reason?: any) => void) => void): Bluebird'.
Here are the relevant definitions from DefinitelyTyped, which ought to be compatible when the type is inferred correctly:
type Resolvable<R> = R | PromiseLike<R>;
constructor(callback: (resolve: (thenableOrResult?: Resolvable<R>) => void, reject: (error?: any) => void, onCancel?: (callback: () => void) => void) => void);
For context, here’s how I’m using the type (unrelated methods omitted):
declare class PromisePool<
A, // the type of value returned by the source
P extends PromiseLike<A>, // the type of Promise returned by the source
P2 extends PromiseLike<A>, // the type of Promise specified in `options`
C extends PromisePool.PromiseClass<A, P2> // a helper class
> {
constructor(
source: IterableIterator<P> | P | (() => (P | undefined)) | A,
concurrency: number,
options?: PromisePool.Options<A, P2, C>
);
promise(): P2;
start(): P2;
}
declare namespace PromisePool {
interface PromiseClass<A, P extends PromiseLike<A>> {
new(callback: (resolve: (value?: A | P) => void, reject: (reason?: any) => void) => void): P;
}
interface Options<A, P extends PromiseLike<A>, C extends PromiseClass<A, P>> {
promise?: C;
}
}
How can I specify the Options
type so that:
- Any
new
able promise library (as above) can be specified, with the types automatically inferred - It falls back to
Promise
if not specified - I can specify the promise type as the return value of
PromisePool.promise
andPromisePool.start