I'm trying to make a library generic across promise libraries, but I've come across a case where, within the same type, a single type parameter is inferred to be two different types:
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>> {
promise?: PromiseClass<A, P>;
}
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`
> {
constructor(
source: P,
concurrency: number,
options?: Options<A, P2>
);
start(): P2;
}
class FakePromise<T> implements PromiseLike<T> {
constructor(
_callback: (
resolve: (value?: T | FakePromise<T>) => void,
reject: (reason?: any) => void) => void) {
}
then<TResult1 = T, TResult2 = never>(
_onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
_onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null):
PromiseLike<TResult1 | TResult2> {
throw new Error("stub");
}
};
// erroneously inferred as PromisePool<unknown, FakePromise<number>,
// PromiseLike<unknown>> instead of PromisePool<number, FakePromise<number>,
// PromiseLike<number>> i.e. A is inferred as both `unknown` and `number`
const pool = new PromisePool(
new FakePromise<number>((_resolve, _reject) => true), 1);
// error:
// Type 'PromiseLike<unknown>' is not assignable to type 'PromiseLike<number>'.
// Type 'unknown' is not assignable to type 'number'.
const promise: PromiseLike<number> = pool.start();
( Playground link. )
It works if I specify the full type of pool
:
const pool: PromisePool<number, FakePromise<number>, PromiseLike<number>> = new PromisePool(
new FakePromise<number>((_resolve, _reject) => true), 1);
How can I tell TypeScript that pool.start
will return a PromiseLike<number>
without having to write it out?
Just get rid of second and third generic arguments (P, P2) from this class:
declare class PromisePool<
A,
> {
constructor(
source: PromiseLike<A>,
concurrency: number,
options?: Options<A, PromiseLike<A>>
);
start(): PromiseLike<A>;
}
Full example:
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>> {
promise?: PromiseClass<A, P>;
}
declare class PromisePool<
A,
> {
constructor(
source: PromiseLike<A>,
concurrency: number,
options?: Options<A, PromiseLike<A>>
);
start(): PromiseLike<A>;
}
class FakePromise<T> implements PromiseLike<T> {
constructor(
_callback: (
resolve: (value?: T | FakePromise<T>) => void,
reject: (reason?: any) => void) => void) {
}
then<TResult1 = T, TResult2 = never>(
_onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
_onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null):
PromiseLike<TResult1 | TResult2> {
throw new Error("stub");
}
};
// erroneously inferred as PromisePool<unknown, FakePromise<number>,
// PromiseLike<unknown>> instead of PromisePool<number, FakePromise<number>,
// PromiseLike<number>> i.e. A is inferred as both `unknown` and `number`
const pool = new PromisePool(
new FakePromise<number>((_resolve, _reject) => true), 1);
const promise = pool.start(); // ok
UPDATE What about this solution:
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>> {
promise?: PromiseClass<A, P>;
}
declare class PromisePool<
A,
T,
> {
constructor(
source: PromiseLike<A>,
concurrency: T,
options?: Options<A, PromiseLike<A>>
);
start(): PromiseLike<T>;
}
class FakePromise<T> implements PromiseLike<T> {
constructor(
_callback: (
resolve: (value?: T | FakePromise<T>) => void,
reject: (reason?: any) => void) => void) {
}
then<TResult1 = T, TResult2 = never>(
_onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
_onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null):
PromiseLike<TResult1 | TResult2> {
throw new Error("stub");
}
};
// erroneously inferred as PromisePool<unknown, FakePromise<number>,
// PromiseLike<unknown>> instead of PromisePool<number, FakePromise<number>,
// PromiseLike<number>> i.e. A is inferred as both `unknown` and `number`
const fake = new FakePromise<number>((_resolve, _reject) => true);
const pool = new PromisePool(fake, 1);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.