Skip to content

Instantly share code, notes, and snippets.

@bisubus
Last active November 20, 2019 23:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bisubus/96f111cb4a41997bc2ff9a2f1cd1006a to your computer and use it in GitHub Desktop.
Save bisubus/96f111cb4a41997bc2ff9a2f1cd1006a to your computer and use it in GitHub Desktop.
Extendable Promise subclass inheritance that works with Babel or TypeScript ES5 target
// inherits from promise class through the chain
new ExtendablePromise(resolve => resolve()).then() instanceof Promise === true
// inherits from promise subclass through the chain
new ExtendablePromise(resolve => resolve()).then() instanceof ExtendablePromise === true
// allows for direct calls for ES5 inheritance
ExtendablePromise.call(Object.create(ExtendablePromise.prototype), resolve => resolve()).then()
class ExtendablePromise {
constructor(executor) {
if (!(this instanceof ExtendablePromise)) {
throw new TypeError("Constructor ExtendablePromise requires 'new'");
}
this._promise = new Promise(executor);
}
catch(onRejected) {
return this.constructor.resolve(this._promise.catch(onRejected));
}
then(onFulfilled, onRejected) {
return this.constructor.resolve(this._promise.then(onFulfilled, onRejected));
}
finally(onFinally) {
return this.constructor.resolve(this._promise.finally(onFinally));
}
}
Object.setPrototypeOf(ExtendablePromise, Promise);
Object.setPrototypeOf(ExtendablePromise.prototype, Promise.prototype);
// Extracted from lib es2015.promise
interface ExtendablePromiseConstructor {
readonly prototype: ExtendablePromise<any>;
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): ExtendablePromise<T>;
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
all<T1, T2, T3, T4, T5, T6, T7>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7]>;
all<T1, T2, T3, T4, T5, T6>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6]>;
all<T1, T2, T3, T4, T5>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>]): ExtendablePromise<[T1, T2, T3, T4, T5]>;
all<T1, T2, T3, T4>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>]): ExtendablePromise<[T1, T2, T3, T4]>;
all<T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): ExtendablePromise<[T1, T2, T3]>;
all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): ExtendablePromise<[T1, T2]>;
all<T>(values: readonly (T | PromiseLike<T>)[]): ExtendablePromise<T[]>;
race<T>(values: readonly T[]): ExtendablePromise<T extends PromiseLike<infer U> ? U : T>;
race<T>(values: Iterable<T>): ExtendablePromise<T extends PromiseLike<infer U> ? U : T>;
reject<T = never>(reason?: any): ExtendablePromise<T>;
resolve<T>(value: T | PromiseLike<T>): ExtendablePromise<T>;
resolve(): ExtendablePromise<void>;
}
class ExtendablePromise<T> {
static all: ExtendablePromiseConstructor['all'];
static race: ExtendablePromiseConstructor['race'];
static reject: ExtendablePromiseConstructor['reject'];
static resolve: ExtendablePromiseConstructor['resolve'];
[Symbol.toStringTag]: string;
protected _promise: Promise<T>;
constructor(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
if (!(this instanceof ExtendablePromise)) {
throw new TypeError("Constructor ExtendablePromise requires 'new'");
}
this._promise = new Promise<T>(executor);
}
catch<TResult = never>(onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): ExtendablePromise<T | TResult> {
return (this.constructor as ExtendablePromiseConstructor).resolve(
this._promise.catch(onRejected)
);
}
then<TResult1 = T, TResult2 = never>(onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): ExtendablePromise<TResult1 | TResult2> {
return (this.constructor as ExtendablePromiseConstructor).resolve(
this._promise.then(onFulfilled, onRejected)
);
}
finally(onFinally?: (() => void) | undefined | null): ExtendablePromise<T> {
return (this.constructor as ExtendablePromiseConstructor).resolve(
this._promise.finally(onFinally)
);
}
}
Object.setPrototypeOf(ExtendablePromise, Promise);
Object.setPrototypeOf(ExtendablePromise.prototype, Promise.prototype);
var ExtendablePromise = function ExtendablePromise(executor) {
if (!(this instanceof ExtendablePromise)) {
throw new TypeError("Constructor ExtendablePromise requires 'new'");
}
this._promise = new Promise(executor);
return this;
}
Object.getOwnPropertyNames(Promise).forEach(function (key) {
if (typeof Promise[key] === 'function') {
ExtendablePromise[key] = Promise[key];
}
});
ExtendablePromise.prototype = Object.create(Promise.prototype);
ExtendablePromise.prototype.constructor = ExtendablePromise;
ExtendablePromise.prototype.catch = function catch_(onRejected) {
return this.constructor.resolve(this._promise.catch(onRejected));
};
ExtendablePromise.prototype.then = function then(onFulfilled, onRejected) {
return this.constructor.resolve(this._promise.then(onFulfilled, onRejected));
};
ExtendablePromise.prototype.finally = function finally_(onFinally) {
return this.constructor.resolve(this._promise.finally(onFinally));
};
// Extracted from lib es2015.promise
interface ExtendablePromiseConstructor {
prototype: ExtendablePromise<any>;
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): ExtendablePromise<T>;
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
all<T1, T2, T3, T4, T5, T6, T7>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6, T7]>;
all<T1, T2, T3, T4, T5, T6>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): ExtendablePromise<[T1, T2, T3, T4, T5, T6]>;
all<T1, T2, T3, T4, T5>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>]): ExtendablePromise<[T1, T2, T3, T4, T5]>;
all<T1, T2, T3, T4>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>]): ExtendablePromise<[T1, T2, T3, T4]>;
all<T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): ExtendablePromise<[T1, T2, T3]>;
all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): ExtendablePromise<[T1, T2]>;
all<T>(values: readonly (T | PromiseLike<T>)[]): ExtendablePromise<T[]>;
race<T>(values: readonly T[]): ExtendablePromise<T extends PromiseLike<infer U> ? U : T>;
race<T>(values: Iterable<T>): ExtendablePromise<T extends PromiseLike<infer U> ? U : T>;
reject<T = never>(reason?: any): ExtendablePromise<T>;
resolve<T>(value: T | PromiseLike<T>): ExtendablePromise<T>;
resolve(): ExtendablePromise<void>;
}
interface ExtendablePromise<T> {
_promise: Promise<T>;
[Symbol.toStringTag]: string;
catch<TResult = never>(onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): ExtendablePromise<T | TResult>;
then<TResult1 = T, TResult2 = never>(onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): ExtendablePromise<TResult1 | TResult2>;
finally(onFinally?: (() => void) | undefined | null): ExtendablePromise<T>;
}
var ExtendablePromise = function ExtendablePromise<T>(this: ExtendablePromise<T>, executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
if (!(this instanceof ExtendablePromise)) {
throw new TypeError("Constructor ExtendablePromise requires 'new'");
}
this._promise = new Promise(executor);
return this;
} as unknown as ExtendablePromiseConstructor;
type TExtendablePromiseMethods = keyof ExtendablePromiseConstructor;
(Object.getOwnPropertyNames(Promise) as (keyof ExtendablePromiseConstructor)[])
.forEach(function (key) {
if (typeof Promise[key] === 'function') {
ExtendablePromise[key] = Promise[key] as any;
}
});
ExtendablePromise.prototype = Object.create(Promise.prototype);
ExtendablePromise.prototype.constructor = ExtendablePromise;
ExtendablePromise.prototype.catch = function catch_(onRejected) {
return (this.constructor as ExtendablePromiseConstructor).resolve(
this._promise.catch(onRejected)
);
};
ExtendablePromise.prototype.then = function then(onFulfilled, onRejected) {
return (this.constructor as ExtendablePromiseConstructor).resolve(
this._promise.then(onFulfilled, onRejected)
);
};
ExtendablePromise.prototype.finally = function finally_(onFinally) {
return (this.constructor as ExtendablePromiseConstructor).resolve(
this._promise.finally(onFinally)
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment