Making distributable flow-typed packages with third-party type dependencies

298 Views Asked by At

I have a library that is using Flow types. It also has a dependency, and part of its interface includes type definitions from the flow-typed file for that library. I'm using flow-copy-source to ensure there are .js.flow files present in the installed package.

The problem I'm having is that when the consumer of this library imports my module, the type imports from the dependency are also imported, but the consumer now has to install its own copy of the flow-typed definitions for that dependency. Normally that'd be fine, but if that consuming code has a different version of that dependency itself, there isn't a way to avoid conflicting definitions.

To put this concretely, my library is an HTTP service wrapper using [email protected] and has imports like this:

import type { Axios, AxiosXHRConfig } from 'axios';

AxiosXHRConfig in version 0.18.x of the flow-typed definition takes two type parameters. In the meantime, the consuming application is using an older version of axios and has a different version of axios library definition for that older version. Since both library definitions have:

declare module "axios" {
  // ...
}

...that import type { ... } from 'axios' in the library doesn't have a way to resolve to the flow-typed/npm/axios_v0.18.x.js in the library instead of the consuming application's flow-typed/npm/axios_v0.17.x.js.

Short of duplicating the flow-typed definitions in the library, how do you make distributable flow-typed packages with global type dependencies that can be safely consumed by other flow projects?

1

There are 1 best solutions below

0
On

I'm taking this approach as a manual workaround until I hear of a more automatic way to do this:

  1. Create a /src/interface.js flow file with only the public interface for my library
  2. Have my client class implement this public interface
  3. Discontinue using flow-copy-source
  4. Publish both my /src and transpiled /lib directory.
  5. Add an index.js and index.js.flow that wipe away the typing for the implementation of the interface, like this:

index.js:

module.exports = require('./lib/client').default;

index.js.flow:

// @flow

import type { FooClient as _FooClient } from './src/interface';
export type FooClient = _FooClient;

const Client: Class<FooClient> = require('./lib/client').default;

export default Client;

This solves the original problem because the third-party dependency is now effectively encapsulated and doesn't reach outside of the exposed interface.