-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Don't include all @types/ automatically, only dependencies from package.json #11917
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
Comments
I like the idea of being more explicit, but personally I'd go further and avoid tying it to |
@blakeembrey Not it is not just you. There are so many different interpretations with so many different answers. ES Modules are pretty silent on this so we can't look to them for guidance. What about Then you have RequireJS, other AMD loaders, and SystemJS. These all have a clearer, although arguably still vague, definition of what a project or package is, with explicitly declared dependencies. This is very different from how Node works as the latter treats the file system as a dependency source, which may make more sense on the server (but is still probably a bad idea), and then falls back to global installations based on environmental variables and so on and so forth. This all leaves a lot to be desired. Something Allen Wirfs-Brock has talked about is the lack of any metadata for ES Modules, maybe something like that in a future specification could help here, but it is difficult to say. |
TypeScript will currently include those types, since it searches for the first
I agree about that, but I'm afraid that that is annoying to users. For instance when they want to use jQuery, they should install jQuery itself, install @aluanhaddad I also don't like to add another configuration file to TypeScript, but, package.json will already contain a list of the used I think that you should make a difference between explicitly resolving packages (using
|
@ivogabe Indeed, I was going off on a tangent about how TypeScript and JavaScript lack a formal definition of projects, packages, and scopes. Regardless, I apologize for going off topic.
|
I agree, which there's already dozens of issues about. I'm suggesting trying to avoid adding new surprising behaviour.
I agree again, but this issue is about the defaults and the open issues suggest the current default could use improving. Sure, every user could just do that, but it's not a valuable default in that case.
I'm not sure I follow this, it's really the same use-case. They are both results of implicit behaviour, but if it helps imagine that I have a parent Edit: To be clearer, I also think your idea is already a better behaviour than the current one, I just want to propose taking it further. Using |
I think this issue is not related to 'definition of project' and should not be resolved by looking at package.json, tsconfig.json, or current root dir. A big part of using es6 module system is to simplify reusing code on client and server. That means you may have one package.json in root folder, a "lib" folder for shared code, a "client" folder for client code, and a "server" folder for server code. The tsc "discovers" the list of files that need to be included in the compilation by walking the import statements. It already has an algorithm for module resolution, which it uses to find the .ts or .d.ts files to be include. All we need here is to add the node_modules/@types/* to the list of directories to search for .d.ts files. In other words, first try to resolve the types in the node_modules/ and then try node_modules/@types with exact same algorithm. |
Those directories are already implicitly in the list of directories that TypeScript enumerates. It is an implicit assumption to resolve packages from there. That is NodeJS behavior, not JavaScript behavior and not ESModule behavior. The point about inferring dependency locations by walking import statements breaks down whenever imports are neither relatives nor absolute. This is the case when importing any external dependency, say |
What I'm suggesting is that loading of these types can follow --moduleResolution value. If module resolution is "node", then the spec defines (https://www.typescriptlang.org/docs/handbook/module-resolution.html) which folders and in what order are searched. I think if the search include the node_module/@types as well and load types files lazily, this particular problem goes away. |
It already includes The only available resolution algorithms are Could you clarify what you mean by load types files lazily? I'm not sure I follow. |
I would like the ability to disable or configure the recursive nature of the search. For example when I set |
What I mean is that compiler should not load all files in the @types folder. Instead, it should only load the content of node_modules/@types/X only if:
In this way, only the types of the modules that are actually referenced in the code are used. The example I provided in #11949 shows a simple case where the code itself is compiled with es5, but an independent test runner modules needs to be compiled (later) with es6 target. These are all part of the same project, but as of now, tsc compilation fails. |
I like the suggestion of @blakeembrey, as it would make everything more predictable. It is more restrictive however. I'd like to hear what others think about it. @aluanhaddad No problem, it does add some important context to this topic. @reshadi What you are suggesting is already implemented for modules (packages that you can include/require). The problem here is: how should ambient/global types (libraries that are exposed as global variables) be loaded? If your code contains The current solution is to include everything in My initial suggestion was to read |
@ivogabe, in any type of application, we can assume there is a main or top module that imports others. One can explicitly say "import '@types/node';" to get the proper types picked up. Same can be done for a browser application. In fact, in this case, you might say the --types flag is redundant since the application could have a simple file that imports all these @types and pass it to the compiler. Imagine, you may also want to have a WinJs, Cordova, Chrome Only, .... type of application. In those cases as well, the "main" of the application can import proper types at first and then import the rest of the code. In fact, we ran into this problem in our project. An external module had an issue with Google Closure. While waiting for upstream to fix it, we use a modified local copy, but for the types, simply use the following to mix local copy with existing types: import './lib_name';
import '@types/lib_name'; setting "types": [] could be a work around, but requires the knowledge of all necessary types to be imported explicitly. |
@mhegazy @DanielRosenwasser @blakeembrey thanks. |
@reshadi You should know all the global types you need to import, that's why I like it being explicit. In what situation wouldn't you know? These are globals/environments, so they shouldn't be controlled by anyone else except you. Reading the Using |
@blakeembrey we have (private) external modules that contain .ts files instead of compiled .js + .d.ts files. The package.json of these other external modules also contain @types. So, npm install will pull in other types that the "current project" is not aware of and come as a dependency. The module resolution does assume that external modules may have .ts files. So, this kind of use case was already considered before. However, the way @types is working now, sometimes can cause issues (as listed here and in #11949). As for explicit imports of types, we just use something like this: This will be normally resolved and works ok. |
@blakeembrey sorry my bad! I just realized that we have a browsrify phase in our build that excludes the @types modules. That's why I had not seen the runtime errors. I thought more about this, even if require @types was doable, still compiler would need a way to associate types loaded this way with the import of actual modules. So, my suggestion may not work and solve that. I agree with you that tying type loading to package.json can still cause problem, specially in cases where client/server code is sitting in one project and have different dependencies captured in the package.json. So, somehow the compiler needs to lazy load the types when it sees modules that it does not know about. |
This case should be covered already with external module usage, and if globals are required it would be covered using |
Just got bitten by this. If a project dependency references class Foo {
private foo: Set<string>;
} This compiles successfully and adds a reference to lodash in the output /// <reference types="lodash" />
export declare class Foo {
bar: Set<any>;
} It should throw an error instead as I didn't reference |
+1 much needed - causing many collisions between libraries |
Took too many searches to find this issue, but the |
Since the compiler knows about |
Global side-effects are visible at runtime, so there is no reason why they should not be visible at design time. The problem arises from declaration files that are authored as global where their .js counter parts do not do that. for that the fix is to change the module declaration. |
@types are included automatically intentionally to simplify the use of the declaration files. Empty So we would like to stick with the current design for the time being. |
true. but the assumption is most users would get the automatic inclusion of |
I just wanted to clarify, in case it was missed, that the biggest issue comes from transitive dependencies and things you did not install. As a result of NPM flattening and TypeScript discovery, any dependency change at any level could break the build. For example, adding or updating a dependency on I know this has bitten myself and has, in the least, bitten enough users of my modules also that the modules I spend time publishing receive the blame instead of whatever the real problem is (see https://github.com/TypeStrong/typedoc/issues?utf8=%E2%9C%93&q=%40types%20dependencies%20 for how many times I've received a PR or issue to move |
I personally have ran into this issue, and my local test script has empty Though it may be inconvenient for bigger projects, we chose to error on the side of simplifying the getting stated scenario. |
@mhegazy The problem is very much globals in type definitions. It's fine for Typescript to automatically read every
In many of these cases, these are global issues that aren't visible at runtime because A) maybe it is a devDependency used only in the dev toolchain, or B) maybe a polyfill is in place or its a typing for a part of code that will never be called if a polyfill isn't in place, but a library wants to provide types for both sides. Polyfills, as one common example, are cases where Typescript's typing system doesn't entirely allow for the variety of real world implementation. DevDependencies and the concepts of multiple environments of code in a single I'd like to see Typescript either:
This issue follows suggestion 1 above, and seems from this distance the easiest of the three possible solutions. Is 2 and/or 3 something better to throw weight behind? |
We looked into this a while back, see #4913. we could not come up with a way that removes the "bad" globals, but keeps the "good" ones like node.d.ts, lib.d.ts, etc..
Either the dependency exports a global, then again it is fair to see it in the global scope; or it does not, and if so, then it needs to be fixed.
that is not true. they are, you get types from them. if your dependency does not expose any of types from its dependency it should not be added. at least that is what we do for publishing @types
see my earlier comment.
yes. but there are others. mocha env, electron env, etc.. |
None of the examples I've seen "pollute the global scope", they are trying to deal with "environment optional types" (where the type itself is optional to the rest of the definition, but wants to be supported if it exists in the current runtime environment) or hybrid environments or parallel environments.
I'm saying it doesn't have enough affordances to describe an accurate interface. That seems pretty clear here to me. There should be some sort of "softer globals" to better represent polyfills and complex environments and parallel environments and environment optional types. The libraries here that have been mentioned causing problems are all environmental issues that Typescript does not model well today. (Module augmentation gets us part of the way, except signatures have to exact match, can change between
I don't know how you structure your projects, but nothing I save to my package.json It would be fantastic if Typescript made it easier to do the right thing with respect to the disparities between these "worlds" automatically, dependencies versus devDependencies (and their dependencies), and avoid "contagions" between them. That's why this issue is here. To some extent I don't care how it gets solved, but whatever the solution, Typescript doesn't have a great story for dealing with global type definitions in
|
That's not quite correct. The npm registry has a fair number of global packages and, significantly, many packages that are dynamic with respect to the environment that they populate at runtime. |
@mhegazy You didn't consider build-time dependencies, which will currently pollute the build. Gulp-typescript provides type definitions, and should also add |
The compiler currently picks all type declarations from
node_modules/@types
. This causes that when a dependency adds some@types
package, this is automatically added to the compilation. However, this is not desired if the dependency is not used, for instance since it is only used in the build process. This could cause a lot of issues; imagine that a dependency of a dependency (etc.) adds an@types
dependency.Real world example: gulp-typescript is now using
@types
for its dependencies;@types/node
is now a dependency of gulp-typescript. This caused that@types/node
is now included in the projects of users of gulp-typescript, and TypeScript will automatically find this.Currently users can either include all
@types
(default) or include a specific list (by setting"types": [ ... ]
. My suggestion is something between those options:Suggestion
Instead of picking all files of
node_modules/@types
, first check if apackage.json
file exist in the current directory or a parent directory. If so, read that file and include all@types
that are registered as a dependency or devDependency. If thepackage.json
file does not exist, fall back to the current behaviour.If a project does include gulp-typescript for instance, the compiler will read the following line:
/// <reference types="node" />
The compiler will automatically include this when run with the
declaration
option. So, types will only be added if they are really used either by the project itself, or by one of the dependencies (recursively).I think that this would solve a lot of issues and make it easier for projects to migrate to
@types
. What do you guys think?The text was updated successfully, but these errors were encountered: