I'm trying to write declarations for Google Maps helper module for node, but I'm having problems with the PromiseConstructorLike that the library expects, and return it's "PromiseLike" instance methods properly (according to https://googlemaps.github.io/google-maps-services-js/docs/module-@google_maps.html):
Promise function <optional> Promise constructor (optional).
so I did (stripped down to the interesting bits):
declare namespace GoogleMaps {
export interface CreateClientOptions<T> {
/** Promise constructor (optional). */
Promise?: T;
}
export interface GoogleMapsClient<T> {
directions<U>(query, callback?: ResponseCallback<U>): RequestHandle<U, T>;
}
export interface Response<U extends any> {
headers: any;
json: U;
status: number;
}
export interface RequestHandle<U, T extends PromiseLike<Response<U>>> {
asPromise(): T;
cancel(): void;
finally(callback: ResponseCallback<U>): void;
}
export type ResponseCallback<U> = (err: Error, result: Response<U>) => void;
export function createClient<T extends PromiseConstructorLike>(options: CreateClientOptions<T>): GoogleMapsClient<T>;
}
declare module '@google/maps' {
export = GoogleMaps
}
of course it doesn't work, if I use for example, Bluebird in the createClient
as
import * as bluebird from 'bluebird'
import { createClient } from '@google/maps'
createClient({ Promise: bluebird }).directions({}).asPromise()/** no "then" here, just the static methods from Bluebird, like Bluebird.all */
The question is then:
Is there anyway I can hint the asPromise
method to return the instance methods (then, catch, finally, reduce, timeout, etc) from bluebird without having to extend the RequestHandle
interface manually?
More info (lib.d.ts
declarations):
PromiseConstructorLike
is:
declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) => PromiseLike<T>;
PromiseLike
is:
interface PromiseLike<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then(
onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null,
onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): PromiseLike<T>;
}
Your declarations contain a compile error, which stems from confusing the Promise
instance type and the Promise
constructor type. Type parameter T
in GoogleMapsClient
is used to fill T
in RequestHandle
, however in GoogleMapsClient
this represents the Promise
constructor type, while in RequestHandle
it represents the Promise
instance type.
It appears to be your intention to make everything properly typed in terms of the Promise
instance type, PromiseLike<Response<U>>
, where U
is the response type. However, since U
is not known in advance (i.e. before calling GoogleMapsClient.directions
), this is unfortunately not possible.
If you want to call then()
after asPromise()
, you can simply change the return type of RequestHandle.asPromise
to PromiseLike<Response<U>>
and remove the type parameter T
:
export interface RequestHandle<U> {
asPromise(): PromiseLike<U>;
cancel(): void;
finally(callback: ResponseCallback<U>): void;
}
I would personally also add the constraint extends PromiseConstructorLike
to type parameter T
in both CreateClientOptions
and GoogleMapsClient
, so that the type-safety of the passed Promise
constructor does not solely depend on the constraint specified in createClient
.
To summarize, the declarations now look like this:
declare namespace GoogleMaps {
export interface CreateClientOptions<T extends PromiseConstructorLike> {
/** Promise constructor (optional). */
Promise?: T;
}
export interface GoogleMapsClient<T extends PromiseConstructorLike> {
directions<U>(query, callback?: ResponseCallback<U>): RequestHandle<U>;
}
export interface Response<U extends any> {
headers: any;
json: U;
status: number;
}
export interface RequestHandle<U> {
asPromise(): PromiseLike<Response<U>>;
cancel(): void;
finally(callback: ResponseCallback<U>): void;
}
export type ResponseCallback<U> = (err: Error, result: Response<U>) => void;
export function createClient<T extends PromiseConstructorLike>(options: CreateClientOptions<T>): GoogleMapsClient<T>;
}
declare module '@google/maps' {
export = GoogleMaps
}
With these declarations, your bluebird
example works and you can call then()
after asPromise()
.
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