JimiDini
JimiDini

Reputation: 2069

Flowtype: dynamically extending classes

Is it possible to manually define additional methods for an existing class?

My specific usecase is bluebird's promisifyAll() which:

Promisifies the entire object by going through the object's properties and creating an async equivalent of each function on the object and its prototype chain… http://bluebirdjs.com/docs/api/promise.promisifyall.html

Obviously, flow wouldn't be able to figure this out automatically. So, I'm willing to help it. The question is HOW?

Consider the following code

import http from 'http'
import { promisifyAll } from 'bluebird'

promisifyAll(http)

const server = http.createServer(() => { console.log('request is in'); })
server.listenAsync(8080).then(() => {
  console.log('Server is ready to handle connections')
})

Flow gives the following error here:

property `listenAsync`. Property not found in
Server

There wouldn't be any error if I used listen. flow's smart enough to see that this is a real method defined in a module. But listenAsync is a dynamic addition by promisifyAll and is invisible to flow

Upvotes: 6

Views: 1970

Answers (1)

vkurchatkin
vkurchatkin

Reputation: 13570

That's not possible and that would not be really safe to do. Here is something you can do for your case:

first declare bluebird as following:

declare module "bluebird" {
  declare function promisifyAll(arg: any): any
}

Then do this:

import httpNode from 'http'
import { promisifyAll } from 'bluebird'
import type { Server } from 'http'


type PromisifiedServer = Server & {
  listenAsync(port: number, hostname?: string, backlog?: number): Promise<PromisifiedServer>;
};

type PromisifiedHttp = {
  createServer(listener: Function): PromisifiedServer;
};

const http: PromisifiedHttp = promisifyAll(httpNode)

Here we manually cast http to type PromisifiedHttp. We still have to declare all promisifed types manually, although we can use type intersection to extends existing types.

Upvotes: 7

Related Questions