Unable to create definition for es6 class exported as default (facebook/dataloader) #670
Description
Prerequisites
- Did you check the FAQ?Did you search open and closed issues to see if your issue has already been reported?
Description
I am trying to write a definition for facebook/dataloader in oder to use it in a TS project.
DataLoader seems to be a es6 class exported as default.
After reading the examples and other articles and issues, still cannot find the way to 'type' the default export and import it as any other class from TypeScript.
I am using TS 1.8.10 with this configuration.
I would like to write a definition like:
declare module 'dataloader' {
// other types omitted
export class DataLoader<K, V> {
(batchLoadFn: BatchLoadFn<K, V>, options?: Options<K, V>): void;
}
}
and use it like:
import {DataLoader} from 'dataloader';
const dataloader = new DataLoader(...);
What is not working as it seems that the import to use must be of the form:
import * as DataLoader from 'dataloader';
which seems to disallow the import of the class.
The question is 'how to write such a definition'? (for a es6 class transpiled into es5 as es6 module but exported as default).
Although probably not a problem with typings itself, I think it would be great to add this case to the examples.
Thanks!!
Alberto
Activity
unional commentedon Aug 21, 2016
You should do
export default class DataLoader ...
Also, you should skip the
declare module 'dataloader' {
as it is a module.Since it is doing
export default
, your usage should be:almilo commentedon Aug 22, 2016
@unional thanks for the help! But I am still not able to make it work.
So far, I have a file with:
If I install it with 'typings install dataloader.d.ts --save', then typings wraps it with a "declare module 'dataloader' {..." what helps tsc find the module when compiling "import DataLoader from 'dataloader';" but in execution fails with "TypeError: dataloader_1.default is not a constructor". What does not make sense to me, as the module seems to export the DataLoader class as default: https://npmcdn.com/dataloader@1.2.0
If I just add the bare file to my index.d.ts to avoid having the "declare module 'dataloader' {...":
then I get the error in tsc: "error TS2307: Cannot find module 'dataloader'." what somehow makes sense too because now there is no TS module defined, right?.
Many thanks for your help!!
Alberto
unional commentedon Aug 22, 2016
IIRC, the last line:
module.exports = exports['default']
will overwrite the exports object.module.exports === exports
If that is correct, then it actually only exports in commonjs format and you have to use it as
I might be wrong though.
almilo commentedon Aug 22, 2016
with
import * as DataLoader from 'dataloader';
orimport DataLoader = require('dataloader');
(they seem to be equivalent) I can get the expected runtime behaviour but tsc does not recognise DataLoader as a class and therefor is of no use as I get compile errors like:dataloader: DataLoader
)new DataLoader(...)
)It seems that tsc only recognises the type information (DataLoader as a class) when I use
import DataLoader from 'dataloader'
(which fails with "TypeError: dataloader_1.default is not a constructor") orimport {DataLoader} from 'dataloader'
(which is inherently wrong as it does not target the 'default' export).Any other ideas?
almilo commentedon Aug 22, 2016
@unional I cloned 'facebook/dataloader' and built it by default (babel5) what generates exactly the same dist file with the
module.exports = exports['default']
at the end. Then I built it with babel6 using the transform-flow-strip-types and transform-es2015-modules-commonjs what generates a similar file but without themodule.exports = exports['default']
at the end. Then I took this line out in my node_modules dataloader and guess what? Now it works, so you were right with your guess.Do you know what is the change between babel5 and babel6 that has originated this problem?
My definition now looks a bit better but it is still weird and feels like a complete workaround:
I have to import it like this:
And use it like this:
Am I still missing something here?
Thanks a lot for the feedback!!
Alberto
almilo commentedon Aug 22, 2016
Just for the record:
The right way of importing a package like facebook/dataloader, which is a es6 class exported as default and which happens to be built with babel5 (as per version 1.2.0) and therefor has a
module.exports = exports['default']
line at the end of the dist/index.js, is as 'commonjs module':Usage in code:
And the type definition should look as shown here
@blakeembrey any feedback on this issue before I close it? Am I completely lost here? or should I send a PR with an example for the documentation to help others falling on this trap?
cheers,
Alberto
blakeembrey commentedon Aug 23, 2016
@almilo Nothing major. I recommend checking out https://www.typescriptlang.org/docs/handbook/modules.html (specifically
export =
andimport =
syntax). Since this module uses themodule.exports =
style export, you need to type this module using theexport =
TypeScript style. To build a definition like that properly, I recommend checking out the page of examples in the docs, but it basically involves creating a namespace with the same name as the primary class so you can re-export interfaces for external access, then usingexport = Dataloader
.almilo commentedon Aug 23, 2016
Finally adjusted my definition as:
and the usage:
Note 1: typescript@1.8.10
Note 2: 'allowSyntheticDefaultImports: true' does not seem to be necessary.
Thanks for the hints! This was neither an easy learning, nor I can say that I love the solution indeed :(
blakeembrey commentedon Aug 23, 2016
If you write the definition using external module syntax (just
export =
at the bottom of the file with outdeclare module
), you can contribute those types back to Typings and re-use them 😄