Allow `--declaration` with `--allowJs`
blakeembrey opened this issue · comments
From #7535, it would be useful for JavaScript package authors to have the ability to generate declarations from their project. This would help with getting started on a type definition (it would be populated with known interfaces/functions/types that are exported) and would also allow the creation of continuous integration scripts that check the JavaScript types with the hand-coded definition for inconsistencies.
PRs are welcomed. the fix should be removing the error, making sure the call is wired correctly, adding tests and making sure the declaration emitter handles the JS types correctly,
anyone currently working on this? If not i may take a stab at it.
anyone currently working on this? If not i may take a stab at it.
got for it.
This would be definitely helpful 👍
👍
Take a look at dts-gen and let us know what you think. At the moment it doesn't do a good job of detecting changes between generations. But let us know if this helps!
Thanks @bowdenk7!
Would a simple approach like #7535 (comment) (internally) be enough to check if the definition has changed? It would be great to be able to run dts-gen
in CI and error when there has been un-documented API changes to prompt an update to the .d.ts
.
Another use-case for this is that I have some plain JS tests that I use as both a form of documentation and as a way of validating that my code is callable from native ES5 in a way that is reasonable. All of my distributed source code is sourced from TypeScript and I would like to generate definition files for NPM publishing. However, because I have that one JS test sitting in my source tree I need allowJs=true
so the test will correctly land in the output directory (not committed to source control) and be run along with all of my other tests. This, of course, means I can't have TSC output definition files for the rest of my project.
@bengreenier Wondering if you have made much progress on this? Let me know if there is anyway I can help speed things up. Would love to see this out in the wild.
Based on the discussion in #15747, we would like to take this change.
@nojvek i've also made no more progress 😁 - got pulled into a different project and had to re-prioritize.
I'm on it. I pulled in your changes to a private branch and working on emitDts only scenario for JS files.
@mhegazy @bengreenier The PR #15911 is still WIP, I am working on tests. wdyt?
@mhegazy I do need your help with some tests though.
It seems the mismatched types here should give an error but they seem not to. This makes a duplicate definition in .d.ts
| //// [tests/cases/compiler/jsFileCompilationDuplicateVariableErrorReported.ts] ////
|
| //// [b.js]
| var x = "hello";
|
| //// [a.ts]
| var x = 10; // Error reported so no declaration file generated?
|
| //// [out.js]
| var x = "hello";
| var x = 10; // Error reported so no declaration file generated?
|
|
| //// [out.d.ts]
| declare var x: string;
->| declare var x: string;
|
It seems the mismatched types here should give an error but they seem not to. This makes a duplicate definition in .d.ts
the input has an error, no guarantees about the output.
- But shouldn't typescript complain about this?
- Are you okay if I modify the baseline to have double declarations then?
But shouldn't typescript complain about this?
there should be an error in the .ts file, and another one in hte .js file with --checkJs
.
following
any updates on that? Current behavior is quite weird : I am using it with webpack 4.20, and webpack config has 2 configurations - development and production. So It actually DOES generate declarations bundle.d.ts and bundle.js itself but only for first configuration, then it throws error "TS5053" and does not even start second (production) configuration. So to point the elephant in the room - can it be fixed simply by changing TS5053 to a warning from error?
This makes it really problematic to reference projects with a tsconfig.json
which had --allowJs
and now must use --declaration
for it to work.
We also just ran into this issue. Any status on how it's progressing? :-)
I’ll take another stab at the PR to deliver a MVP .js -> .d.ts types. We’ve converted a ton of things to .js with jsdoc types and this would be quite useful to the rest of the community.
I think you can already do that with dts-gen, but it would be great to have the functionality in TypeScript itself.
@nickmccurdy It looks like dts-gen
hasn't been updated in over a year and still uses TS 2.x
dts-gen
is preferable for a certain set of people (and its functionality is now a quickfix in the compiler, which is why the standalone project isn't maintained so much) - it inspects the types it can find at runtime, and so can be pretty accurate with no annotations present. What this feature is for, I imagine, is people using mostly jsdoc
to strongly type their JS, who want their documentation and types preserved and transformed into a public API.
While generating types from javascript would be useful, is there a way to simply skip the javascript files when emitting types? Surely emitting some types is better than nothing at all.
As a workaround, I used tsc
to generate dist folder with allowJs: true
in tsconfig.json
and then used tsc -d --emitDeclarationOnly --allowJs false
to generate typings in dist
folder.
This would be very very nice to have as now that we have completely typed out our webpack configuration and types for our public api, exposing this on a single d.ts would mark a lot of progress for thrid party integrations and plugin authors. :-) <3 @DanielRosenwasser @weswigham :-)
@talhajunaidd Thanks, It's work for me. Also I wrote a blog for those who want help:https://shuoit.net/tech-notes/Allow--declaration-with--allowJs-1546511333.html
For what it is worth, I recently discovered over at
#29056 (comment)
That if a consumer sets maxNodeModuleJsDepth
to say 5
in their tsconfig, then typescript is likely able to pickup the jsdoc types of the packages they consume automatically, without the need for any d.ts file. Which is likely a suitable workaround for many here.
I've updated https://github.com/bevry/projectz to automatically generate an installation guidance in readmes of maxNodeModuleJsDepth
to 5
if the project it is running against has jsdoc in its dependencies.
The result of this generation can be seem on the caterpillar package for example:
https://github.com/bevry/caterpillar
@DanielRosenwasser Was help wanted removed because this is now being considered as a feature worked on by the TypeScript team?
It would be a great step forward if we could export the jsdoc typings from webpack into a distributable definition file and I guess the same is true for many other open source projects.
Am I understanding it correct that you simply cannot use declaration
, and hence cannot package reusable libraries if you use allowJs
to consume JsDoc-based .js files?
My use case is that I want to create an Angular component library which uses OpenLayers, which recently moved to a TypeScript-compatible JsDoc in 6.0.0-beta.1.
But as I read this issue, this is simply not possible, as allowJs
precludes the use of the needed declaration
for a distributable Angular library?
Just a kind ping. Is this something that is tracked in the Roadmap or still awaiting someone from the community to make a pass at this?
you said
That if a consumer sets
maxNodeModuleJsDepth
to say 5 in their tsconfig, then typescript is likely able to pickup the jsdoc types of the packages they consume automatically, without the need for any d.ts file.
No need to use maxNodeModuleJsDepth
. For mongoose project, simply use include
and paths
options like described at https://github.com/openlayers/openlayers#intellisense-support-and-type-checking-for-vs-code or openlayers/openlayers#9178 (comment) (for .tsconfig
). Tested and work fine ! As it, tsc
will no parse no useful modules, because with maxNodeModuleJsDepth
you parse all the node_modules
, could become very huge and not very good ! See #17183
So are we just dead in the water on this? Three years? I'm trying to add a library to a existing Angular 7 project with two apps and the newly generated library with no modification throws error TS5053: Option 'allowJs' cannot be specified with option 'declaration'.
I am also facing this issue when trying to transpile .ts
files inside of node_modules
while also generating an index.d.ts
for a webpack bundle.
My use case is that I want to publish a node lib which is vanilla JS with JSDoc. I'm using typescript for typechecking and I was hoping that it could generate the .d.ts
typings as well but nope.
Agree w/ @phaux . Our company has over a dozen internal JS packages that have excellent JSDoc coverage. If we could generate typings for those, it would make it much easier to get executive buy-in to make the leap to TS.
Actually it would be nice if typescript was smart enough to look into current project jsdoc'd dependencies and just use these. Currently it screams something about missing definitions for dependency even if it has jsdocs. It could look at source/module/main package.json field and just look at it as a regular file and get typings from there.
It's 30th of August and still no fix? What a shame.
For what it's worth, in most cases I think it's possible to work around this, and at least in the cases I've come across, it's probably preferable to work around it. Not so much as a workaround as just doing things in a clean way.
- If you're using typescript to provide typechecking for your code, and you want to emit a .d.ts - change the file extensions from 'js' to 'ts'. Turn off allowJs.
- If you're generating code that's valid JS but invalid TS (this was my case), you might be able to just get away with using a
require
instead of importing. Turn off allowJs. (Even better would be to generate valid TS, but this was a bit tricky in my case, so went the hacky route instead -require
worked just fine, and by wrapping in a TS file which applied some types, I was able to quarantine the hackiness to a single file.) - If you have JS files that are only used for certain build / validation processes, use a separate tsconfig.allowJs.json that extends your tsconfig.json with allowJs=true, and have allowJs=false in your main tsconfig.json.
- If you've got a shared library you want to use, publish it as an npm module with types (or publish the types separately). You don't need allowJs=true to import an npm module.
In many of these cases it'd be great if it was possible to just use the JS directly, of course, but if you're feeling blocked by this, rest assured there are ways forward :-) If you've got a case that's not covered by this @mention me and I'll do my best to help.
The primary use case I had in mind is when you are using typescript only for typing through vanilla javascript and JSDoc. Typescript turns into a great typing annotation engine, and it knows all the types .. and instead of building a declaration file yourself, it should be able to to generate one.
Hmm yes, had a play with that just now. It's really a "so close and yet so far" situation isn't it?
An answer still might be to bite the bullet and shift to .ts
. You might consider using something like ts-morph to make this less painful. A bit of a micky-mouse example:
// test.js
export class MyClass {}
/**
*
* @param {string} a
* @param {MyClass} b
* @returns {number}
*/
export function blah(a, b) {
return 1;
}
And then you can make the type annotations explicit and change the extension to ts using ts-morph
// transform.ts
import { Project } from "ts-morph";
const project = new Project({
tsConfigFilePath: "./tsconfig.json",
addFilesFromTsConfig: false
});
// will want to have a better way to add all the files you want to transform...
const file = project.addExistingSourceFile("./test.js");
// I guess you'd need to change more than just functions, but for a proof of concept...
const functions = file.getFunctions();
functions.forEach(fn => {
const parameters = fn.getParameters();
parameters.forEach(param => {
// get typescripts best guess of what type the param is
const type = param.getType();
// and make it explicit
param.setType(type.getText());
});
// likewise for return type
const returnType = fn.getReturnType();
fn.setReturnType(returnType.getText());
});
// move the file
file.move("./test.ts");
// save the project - will write out all the files that have changed.
project.save();
This issue is important because nobody should have to bite a bullet to get value out of tsc without making any substantive changes.
iow, the point is so you don’t have to convince people to author anything in typescript before they can try out the value of a type system.
There's also technical value in fixing this, namely enabling using --incremental
(and --composite
) for projects with --allowJs
, imo.
@weswigham @ljharb - not trying to argue this isn't valuable, I'd have loved it when I needed it. Just trying to identify some other approaches.
@industrialCoder would it be possible to change the rest of the files across to .ts and apply any
yourself?
@studds when I say a large legacy library, I mean a very large legacy library... The time it would take to convert all of the .js files over to .ts and add the syntax required to make them valid TS (even typing everything as any) would be obscene to say the least. Is saying type = importSourceType == js ? any : determineTSType(source)
really that huge of a lift? Not familiar with the TS compiler code, but seems like this would be good bandaid solution that would help a lot of people out and should be much easier to implement than the solutions a lot of other people here are purposing....
@amitbeck I solved it for now by generating types separately and using --allowJs=false on the type generation step.
I have an issue opened on TypeScript problems with understanding imported JSDoc types from imported node modules. It's currently flagged as backlog. Feel free to comment there to push this along: #33136. Getting this fixed would allow JavaScript authors to provide JSDoc types for their code that end users could take advantage of for type linting and Intellisense in VSCode with allowjs
set to true.
At Unsplash, this is blocking us from using project references aka composite
(because composite
requires declaration
to be turned on).
Glorious day! Thanks @weswigham! Looks like this is slated for 3.7? Man that is going to be a great release.
Does the conversion mechanism recognize node packaging? It seems it generates one module per file, although Node.js exports one module per dependency.
A module is a file is a module; an npm package has 0, 1, or N files/modules.
You are right. Still, it does not help with producing declarations compatible with the node module lookup mechanism. You may want to refer to this question on StackOverflow for an example of what is precisely happening: https://stackoverflow.com/questions/59564524/generating-typescript-declarations-for-re-exported-js-functions-in-a-node-js-mod
is this resolved?