Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Declare default export among other exports #1806

Closed
rpominov opened this issue May 18, 2016 · 18 comments
Closed

Declare default export among other exports #1806

rpominov opened this issue May 18, 2016 · 18 comments
Labels

Comments

@rpominov
Copy link

rpominov commented May 18, 2016

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
}
@jeffmo
Copy link
Contributor

jeffmo commented May 18, 2016

Unfortunately this is a shortcoming of our declare module syntax at the moment: Right now all declare modules 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!

@jeffmo jeffmo self-assigned this May 18, 2016
@jeffmo jeffmo added the bug label May 18, 2016
@rpominov
Copy link
Author

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.

@jeffmo
Copy link
Contributor

jeffmo commented May 19, 2016

@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,
  };
}
  1. 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;
}

@rpominov
Copy link
Author

Thanks! Can't check at the moment, but I think both methods should work great in my case 👍

@jeffmo
Copy link
Contributor

jeffmo commented Jul 12, 2016

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";

@jeffmo jeffmo closed this as completed Jul 12, 2016
@Macil
Copy link
Contributor

Macil commented Jul 13, 2016

Is there any difference between these two?

declare module "my-module" {
  declare export var foo: string;
}
declare module "my-module" {
  declare var foo: string;
}

@tomitrescak
Copy link

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;
}

@rpominov
Copy link
Author

rpominov commented Aug 17, 2016

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

@pgherveou
Copy link

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
}

@tomitrescak
Copy link

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
}

@jeffmo
Copy link
Contributor

jeffmo commented Aug 19, 2016

@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

@dtothefp
Copy link

dtothefp commented Sep 1, 2016

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

@peterhal
Copy link
Contributor

I get a parse error on:

declare export var ...

@jeffmo
Copy link
Contributor

jeffmo commented Oct 29, 2016

@trueter
Copy link

trueter commented Jan 3, 2017

@jeffmo I'm also getting the parse error. Might have something to do with the babel parser for eslint?

@jedwards1211
Copy link
Contributor

jedwards1211 commented Feb 2, 2017

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

@jedwards1211
Copy link
Contributor

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...

@0xcaff
Copy link

0xcaff commented Jan 6, 2018

How would you export a variable with a hyphen with the declare export var syntax?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants