Suppose there is an npm module like this:
// index.js
import React from 'react'
export default React.createClass({...})
export const foo = 'bar'
And I want to write a declaration file for this module. If it only has the default
export I would write something like this:
// my-module.js.flow
declare module 'my-module' {
declare var exports: React.Component
}
If it doesn't have the default
export, I would do:
// my-module.js.flow
declare module 'my-module' {
declare var foo: string
}
But in combination this doesn't work:
// my-module.js.flow
declare module 'my-module' {
declare var foo: string
declare var exports: React.Component // this overrides foo
}
Unfortunately this is a shortcoming of our declare module
syntax at the moment: Right now all declare module
s are interpreted as if they were CommonJS modules, and CommonJS modules only have a single export (so you there's no way to express both a default and named exports at the same time).
The plan to address this is to add export
syntax to declare module
. I will start to work on this today, thanks for the reminder!
That's great news that you're starting working on this. Thank you!
In the meantime, is there a way to add type info somehow for such module? I'm experimenting with using module.name_mapper
to replace the file from which Flow gets type info for this module, and this seems to work. Still wondering if there is a better way.
@rpominov: If there is no way for you to change the upstream module directly right now then the only 2 workarounds I can think of are:
1) Use require()
directly in the importing file until the Flow fix lands (if this is an option in your setup?). With this (assuming your system uses standard ES/CJS interop semantics like Babel does) you could define the interface as:
my-wrapper-module.js
declare module 'my-module' {
declare module.exports: {
default: React.Component,
foo: string,
};
}
2) Create an untyped wrapper module around the external module that hoists the default export to a named export. Then you can write a declaration file for that wrapper:
my-wrapper-module.js
export {default as component, foo} from "my-module";
my-wrapper-module.js.flow
declare module 'my-module' {
declare var component: React.Component;
declare var foo: string;
}
Thanks! Can't check at the moment, but I think both methods should work great in my case 馃憤
Ok, this has been fixed as of v0.28. You can now do the following:
declare module "my-module" {
declare export default React.Component;
declare export var foo: string;
}
import DefaultReactComponent from "my-module";
import {foo} from "my-module";
Is there any difference between these two?
declare module "my-module" {
declare export var foo: string;
}
declare module "my-module" {
declare var foo: string;
}
Is this supposed to work ? I tried on 0.30.0. But not of below works and I'm getting errors:
declare module "my-module" {
declare export var exp: string;
declare export default exp;
}
declare module "my-modules" {
declare var exp: string;
declare export default exp;
}
You should do it like this (only one line needed):
// @flow
declare module "my-modules" {
declare export default string;
}
// @flow
import a from 'my-modules'
(a: number)
6: (a: number)
^ string. This type is incompatible with
6: (a: number)
^^^^^^ number
To complete the answers above, you can do one of the following form to define your modules
// usage:
// import foo from 'some-module'
declare module 'some-module' {
declare type Foo = {/*...*/}
declare export default Foo
}
// usage:
// import myFunc from 'some-module'
declare module 'some-module' {
declare export default () => void
}
// usage:
// import { foo } from 'some-module'
declare module 'some-module' {
declare type Foo = {/*...*/}
declare export var foo: FooType
}
// usage:
// import Foo from 'some-module'
declare module 'some-module' {
declare class Foo {}
declare export default typeof Foo
}
One more that seems to be even compatible with eslint:
declare module 'some-module' {
declare function f(): void;
declare var exports : typeof f; // and all type alternatives
}
@tomitrescak: declare var exports
is deprecated and will be going away soon. Seems we'll want to get declare export default
supported in eslint ASAP
I'm new to flow but just an FYI related to this issue the EventEmitter
defined in lib/node
https://github.com/facebook/flow/blob/master/lib/node.js#L581 seems to need updating to this type of syntax since
import EventEmitter from 'events'
//or
import {EventEmitter} from 'events'
got it to work with suggestions above
declare module "events" {
// TODO: See the comment above the events$EventEmitter declaration
declare class EventEmitter {
static EventEmitter: typeof EventEmitter;
// deprecated
static listenerCount(emitter: events$EventEmitter, event: string): number;
addListener(event: string, listener: Function): events$EventEmitter;
emit(event: string, ...args:Array<any>): boolean;
listeners(event: string): Array<Function>;
listenerCount(event: string): number;
on(event: string, listener: Function): events$EventEmitter;
once(event: string, listener: Function): events$EventEmitter;
removeAllListeners(event?: string): events$EventEmitter;
removeListener(event: string, listener: Function): events$EventEmitter;
setMaxListeners(n: number): void;
getMaxListeners(): number;
}
declare export var EventEmitter: typeof EventEmitter
declare export default typeof EventEmitter
}
hopefully that saves someone some time
I get a parse error on:
declare export var ...
@peterhal: Are you writing it like this?
https://flowtype.org/try/#0CYUwxgNghgTiAEBbA9sArhBAiAsl+A3gFDzyiSwIgAeADsjAC7wBus8AdgFydqIBGIGAG4iAXyA
@jeffmo I'm also getting the parse error. Might have something to do with the babel parser for eslint?
For those using index.js.flow
to type a class as module.exports
(i.e. can be gotten via require('index.js')
instead of require('index.js').default
), I was able to do the following:
index.js.flow
// @flow
declare class MyClass
// Flow gripes that typeof MyClass isn't compatible with exports...yet it seems to do the
// proper typechecking with it everywhere else!
// flow-issue
declare var exports: typeof MyClass
Is there a way to import a default type from a CommonJS module?
I'm trying to do
declare module 'superagent' {
declare class Superagent {
(method: string, url: string): Request;
get(url: string): Request;
head(url: string): Request;
put(url: string): Request;
post(url: string): Request;
delete(url: string): Request;
patch(url: string): Request;
options(url: string): Request;
trace(url: string): Request;
}
declare module.exports: Superagent;
}
but then import type Superagent from 'superagent'
doesn't work...
How would you export a variable with a hyphen with the declare export var
syntax?
Most helpful comment
Ok, this has been fixed as of v0.28. You can now do the following: