Generate a single declaration file for external modules #2568
Copy link
Copy link
Closed
Labels
FixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript
Milestone
Description
If compiling external modules, with --declaration, each module gets its own declaration file. this is not useful for library authors, who probably want to hand off a single .d.ts file for all their modules. the only way to do this now, is to either hand edit the file, or use a tool to concatenate the output.
This becomes even more desirable with ES6 module export/import where a module can reexport exports of another module.
See discussion in #2516 (comment) for more details.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
FixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript
Activity
mhegazy commentedon Mar 31, 2015
Also related to #17
basarat commentedon Mar 31, 2015
Tagging Colin Snover (@csnover) because of https://github.com/SitePen/dts-generator
NoelAbrahams commentedon Apr 1, 2015
I would like to have a
generate-single-declaration-fileoption (without combining JavaScript output) for _internal modules_ as well. See #192.ToddThomson commentedon Apr 2, 2015
My experiences with Typescript 1.5 have been positive. The only thing that keeps giving me grief is the internal "module" ( which is only a namespace ). I really don't like fighting back and forth between internal and external/real/es6 modules and what I am allowed to do with both ( mostly the grief that comes from defining external module bundles ). I really only want to use the ES6 module syntax going forward.
I have no issues with 1 file = 1 es6 module and optimizing during a production build. However there are times that I want to bundle files/modules together at compilation time. Even if this just produced a named typescript definition file I would be happy.
I am wondering if you can leverage the tsconfig.json "project" file to provide "bundles". A bundle would look like:
"bundles": {
"bundle-name": [ "some/module", "another/module" ],
"another-bundle-name": [ "yet-another-module" ]
}
An appropriate arg could be used for command line builds.
ToddThomson commentedon Apr 17, 2015
I've put together an npm module ( TsProject ) that uses the tsconfig.json file to define bundles. It generates a typescript source file based on the dependencies in a source file as specified by a bundle. The generated typescript source file is output along with the compiled javascript and definition file.
poelstra commentedon Apr 19, 2015
I'm not too sure it's really "a single .d.ts file" that we'd need.
Having one .d.ts file per .emitted .js file is a very good fit for the CommonJS way of working.
Relevant advantages I see:
Note that with external modules, the name of an external module is typically not present in the file itself. It is determined by its filename. So, I'd vote for keeping the currently generated .d.ts files as-is, and not e.g. put 'declare module "my-module" {}' inside of them, not even in an 'overall bundle file'.
Tools like dts-bundle and dts-generator seem to prefer to write out one file, but I believe this is mainly because TypeScript doesn't allow specifying relative paths for the import()'s in .d.ts files.
I think that if TSC would allow those paths even in .d.ts files, dts-bundle or dts-generator would no longer be necessary.
Still, a solution needs to be found for any other 'really external' modules (i.e. different npm packages, not just different .ts files inside the package) that are referenced, as this would basically mean that the /// <reference ...> line of the package would need to be 'local' to that package's .d.ts file. Otherwise these really external modules will start conflicting with similar modules in other packages.
TSC disallows having an external module be declared multiple times, which you can only get to work if all references are pointing to the same definition file. So, let's not try to 'throw away' that information by bundling, but support the case of leaving the files where they are, and use the 'real' filesystem to resolve the references.
Note: my use-case is mainly 'plain' CommonJS, using TSC's --outDir option, and using browserify for the browser. I guess that the single-.d.ts-file case would be handy for TSC's --out option.
basarat commentedon Apr 20, 2015
Worth sharing here : #2829 all it needs is the path to your
package.json+tsconfig.jsonto generate and consume node packages. no additional configuration required.basarat commentedon Apr 20, 2015
Martin Poelstra (@poelstra)
Not true. Look at : https://github.com/TypeStrong/atom-typescript-examples/blob/master/node/lib/main.ts You can import any file you have exported despite using a single
.d.tsdefinition fileAgain the details of the proposal : #2829
poelstra commentedon Apr 20, 2015
Basarat Ali Syed (@basarat) I know about that possibility, yes, and in fact, that's what we use today (using output of dts-bundle at work, and recently started using dts-generator at home).
The problem I see with it though, is that it will break as soon as more than one dependency level is introduced.
Consider this dependency tree:
In the case of generating .d.ts files with
declare module "myutils" { ... }, etc, all of these declarations will be in the same 'global module name namespace'. Thus, mylib and myotherlib cannot each use their own version of myutils.This is not a problem as long as myutils is only used internally (and thus its types are never exposed through mylib/myotherlib), but that will often not be the case.
basarat commentedon Apr 20, 2015
Martin Poelstra (@poelstra) awesome. Perhaps you can hash out what a
.d.tswould look like formylib/myotherlib/myutils@1.0/myutils@2.0Then I can have something to work from and understand your proposal better (perhaps then I can even implement it).
20 remaining items
bennycode commentedon May 31, 2017
Mohamed Hegazy (@mhegazy), how can declaration output files be bundled?
I followed up on #5090 and there it's mentioned that you need to set
--modulealong with--outFile. Unfortunately this doesn't work with"module": "commonjs"because of:mhegazy commentedon May 31, 2017
why do you want to bundle your declarations if you are using node? the .js files are not bundled either.
delagen commentedon May 31, 2017
Mohamed Hegazy (@mhegazy) if TS itself cannot bundle JS files, that does not mean that no any other tool.
JS files can be bundled with
rollup,webpackor any other bundler.For now I bundle declarations using
typingstool, that works, but result all declarations in file, not only exported by main entry. Result declaration bundle contains from manydeclare modulesections with add main with package.json module name. But this is more convenient for me instead of place one compiled module (sometimes with sourcemap file) in package uploaded to registry with manyd.tsfiles generated by TS compile.bennycode commentedon Jun 15, 2017
Mohamed Hegazy (@mhegazy) I am getting lost. Maybe you can help me.
I have a TypeScript application with the following configuration (
tsconfig.json):{ "compilerOptions": { "declaration": true, "lib": ["DOM", "ES6"], "module": "CommonJS", "moduleResolution": "node", "noEmitOnError": true, "noImplicitAny": true, "noImplicitReturns": true, "outDir": "dist/commonjs", "removeComments": true, "rootDir": "src/ts", "sourceMap": false, "target": "ES5" }, "exclude": [ "dist/commonjs", "node_modules" ] }When I run
tsc, then the TypeScript compiler creates several JavaScript files (*.js) and several TypeScript definition files (.d.ts):Now I want to distribute my code as npm package and I would like to support types for people using my code but I don't know where I should point the
typesproperty in mypackage.jsonto.The documentation says: "Set the
typesproperty to point to your bundled declaration file" but I don't have a definition bundle (likemain.d.tsorindex.d.ts). Instead I have several*.d.tsfiles. 😕So what can I do now to publish my npm package along with it's definitions?
delagen commentedon Jun 15, 2017
bennyn You can publish it as is. (all .js and .d.ts files)
orefalo commentedon Jun 15, 2017
bennyn I have the same issue
Sergey Vohmyanin (@delagen) How? file: [ "dist/" ] ?
ikatyang commentedon Jun 15, 2017
bennyn Olivier Refalo (@orefalo)
For short,
typesis themainfor types.Just like the
mainfield inpackage.json, you have to specify the declaration file for themainfile, then TS will follow the module resolution (import/export) to find those related type.For example:
(package.json)
{ "main": "lib/index.js", "types": "lib/index.d.ts" }delagen commentedon Jun 15, 2017
Olivier Refalo (@orefalo) if file d.ts place is near the main js the types section is unneded. TS compiler try to load declaration with the JS file loaded, so no action needed.
If declaration file is different named than main file, so it must be specified in types property of package.json.
bennycode commentedon Jun 16, 2017
Ika (@ikatyang) I have done what you have suggested. In my library I pointed
typesto myindex.d.tsfile:package.json
./dist/commonjs/index.d.ts
Afterwards I tried to use my library in another TypeScript project:
This caused the following error:
So I changed my import statement:
Which then led to the following errors:
LoginDataandAccessTokenDataare classes from my library and they are in the same directory asAuthAPI, that's why there is no import statement inAuthAPIfor them. So it seems like the TS module resolution doesn't work properly in my case?ikatyang commentedon Jun 17, 2017
bennyn I have no idea what's going on to your compiled dts file, since I cloned your PR and execute
yarn run dist, I found that your compiledAuthAPI.d.tssomehow miss the triple-slash directives.(AuthAPI.d.ts)
It compiled fine for me (with triple-slash directives exist), and thus the following import works fine.
NOTE: There are several unlinked definitions in your code (does not use
importortriple-slash-directives, so TS cannot find them), you have to correct them to get the result.bennycode commentedon Jun 18, 2017
Ika (@ikatyang) Thank you so much for your support!
I forgot to regenerate the
.d.tsfiles in the@wireapp/api-clientproject. After I did that everything was running smooth. I've also found a better alternative to the triple-slash directives:Before (no interface export + triple-slash)
LoginData.ts
AuthAPI.ts
After (interface export + interface import)
LoginData.ts
AuthAPI.ts
Outcome
With
export default LoginData;I can now use the@wireapp/api-clientlibrary like this:This is exactly what I wanted to do. Thank you very much! 🍻