{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4665","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4665/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4665/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4665/events","html_url":"https://github.com/Microsoft/TypeScript/issues/4665","id":105047101,"number":4665,"title":"Resolving dependency conflicts when importing node package typings","user":{"login":"mhegazy","id":8000722,"avatar_url":"https://avatars.githubusercontent.com/u/8000722?v=3","gravatar_id":"","url":"https://api.github.com/users/mhegazy","html_url":"https://github.com/mhegazy","followers_url":"https://api.github.com/users/mhegazy/followers","following_url":"https://api.github.com/users/mhegazy/following{/other_user}","gists_url":"https://api.github.com/users/mhegazy/gists{/gist_id}","starred_url":"https://api.github.com/users/mhegazy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhegazy/subscriptions","organizations_url":"https://api.github.com/users/mhegazy/orgs","repos_url":"https://api.github.com/users/mhegazy/repos","events_url":"https://api.github.com/users/mhegazy/events{/privacy}","received_events_url":"https://api.github.com/users/mhegazy/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/In%20Discussion","name":"In Discussion","color":"556b75"},{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/Suggestion","name":"Suggestion","color":"006b75"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":29,"created_at":"2015-09-05T20:22:33Z","updated_at":"2015-09-18T00:11:45Z","closed_at":null,"body":"This issue is explained in details in https://github.com/Microsoft/TypeScript/issues/2839\r\n\r\n\r\n## Terminology\r\n\r\nThrough out this issue, i will be referring to different ways of declaring external modules, here are what i mean by them.\r\n\r\n#### Ambient external module declaration\r\n\r\nModule declarations of the form:\r\n\r\n```ts\r\ndeclare module \"mod\" {\r\n\t....\r\n}\r\n```\r\n\r\n#### Proper external modules\r\n\r\nThe other way of writing a declaration of a module, i.e. a declaration file with a top level `import` or `export`; e.g.:\r\n```ts\r\nimport { Contract } from \".\\core\"\r\nexport var MyContract: Contract;\r\n```\r\n\r\n#### Script file\r\n\r\nA file that is not a module, i.e. does not include a top level `import` or `export`. it could however, include ambient external module declarations as described above.\r\n\r\n## Background\r\n\r\nWith module resolution work done in #4154 and #4352, it is possible for node package authors to distribute typings with their packages. For these packages, users do not have to independently acquire the typings from definitely typed, but will just `import` the package, the the compiler will locate the typings in the package directory (either `index.d.ts` or by following `typings` property in `package.json` in the package directory).\r\n\r\nAs noted in #2839, for a package author interested in distributing their typings with their package, there are dependencies that they need typings for; the most common would be `node.d.ts`, which is possibly referenced by each package.\r\n\r\nThe way they dependency typing are authored today on definitely typed is using *ambient external module declaration*; this indicates that these typings exist in the global scope, and thus are susceptible to conflicts. The classic the diamond dependency issue (courtesy of @poelstra):\r\n\r\n- myprogram\r\n  - mylib\r\n    - myutils@1.0\r\n  - myotherlib\r\n    - myutils@2.0\r\n\r\nNow, if `myUtils.d.ts` uses *ambient external module declaration*, this is guaranteed to result in re-declaration errors.\r\n\r\n## Proposal\r\n\r\nWhen resolving an `import` target in a node module, it is an **error** to include files that are **not** either:\r\n1. **Proper external modules**, or\r\n2. A **Versioned script file** - see below\r\n\r\nFor proper external modules, there are no conflicts expected, as external modules do no polute the global namespace.\r\n\r\nFor versioned files, referring to the same library, the compiler will follow a *conflict resolution* logic picking only the **latest** copy (as described by @basarat in https://github.com/Microsoft/TypeScript/issues/2839#issuecomment-94630741). The files provided on the command line will be allowed to override this conflict resolution policy.\r\n\r\n## Versioned files\r\n\r\nThese are files declaring an **identity**, i.e. a name and a version. A versioned file is expected to contain a comment at the top of the form\r\n```ts\r\n/// <library name=\"myLibraryName\" version=\"1.0.23.2-beta\" />\r\n```\r\n\r\nWhere the `name` field is optional, and if committed, the file name is used instead, and the `version` field follows the version definition in http://semver.org/\r\n"},"comment":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/comments/141292543","html_url":"https://github.com/Microsoft/TypeScript/issues/4665#issuecomment-141292543","issue_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4665","id":141292543,"user":{"login":"weswigham","id":2932786,"avatar_url":"https://avatars.githubusercontent.com/u/2932786?v=3","gravatar_id":"","url":"https://api.github.com/users/weswigham","html_url":"https://github.com/weswigham","followers_url":"https://api.github.com/users/weswigham/followers","following_url":"https://api.github.com/users/weswigham/following{/other_user}","gists_url":"https://api.github.com/users/weswigham/gists{/gist_id}","starred_url":"https://api.github.com/users/weswigham/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weswigham/subscriptions","organizations_url":"https://api.github.com/users/weswigham/orgs","repos_url":"https://api.github.com/users/weswigham/repos","events_url":"https://api.github.com/users/weswigham/events{/privacy}","received_events_url":"https://api.github.com/users/weswigham/received_events","type":"User","site_admin":false},"created_at":"2015-09-18T00:11:45Z","updated_at":"2015-09-18T00:11:45Z","body":"I'm about to be way more verbose than I've been in this issue and the related competing proposal in the past.\r\n\r\n> @weswigham The problem with what you're suggesting is that it completely disregards the use of libraries as globals in the browser. If all that TypeScript did was node-style modules then I would agree. But library authors (or definition authors) need to provide typings generally both for node-style modules and browser globals.\r\n\r\nThat's precisely why we need to find a way to continue allowing ambient external declarations while continuing to improve the package consumer use case. This is why I suggest _package_-scoping ambient declarations by default. When using `.d.ts` files in a browser context, _this doesn't come up as a problem because you're not using packages_! :smile: All ambient declarations are global scoped when there are no packages! _Nothing we're discussing here will ever effect browser or flat project consumers._ Unless you `import \"foo\"` and \"foo\" is a node package, none of the logic or issues discussed here should ever come into play. Nor those discussed in any of these related proposals.\r\n\r\nHere's what I think. People want to reuse their handwritten browser definitions. We should _allow_ ambient module declarations of the style\r\n`foo.d.ts`:\r\n```ts\r\ndeclare module \"foo\" {\r\n   //...\r\n}\r\n```\r\nWithin packages. And we should continue to disallow _directly_ having ambient definitions like that at the entrypoint specified in the \"typings\" field of a \"package.json\". However, we could then _allow_ (or encourage) the following:\r\n`index.d.ts`:\r\n```ts\r\n/// <reference path=\"foo.d.ts\"/>\r\nexport * from \"foo\"; //pulls the \"foo\" package from the package scope and exports it to the caller\r\n```\r\n(First, re-enabling triple-slash references in packages.) And all _ambient_ declarations made _within_ a package are scoped to only _within_ that package - this way when you include a package, you only get members intentionally exported by the package. The ambient module \"foo\" isn't accessible to your child packages or your parent package - it's as if you declared the types inline in your file. That's the key here. This way my package can include and declare any number of `.d.ts` files for it's own internal consumption (so I can use underscore or rxjs with types internally or whatever), but then only expose to the package consumer those types which my package _actually exposes via its JS_.\r\n\r\n_Intentional_ modifications to the global scope aren't currently possible within external modules - that's a separate but related issue. It's also a comparatively rare use-case within packages, and is lower priority than allowing reuse of existing `.d.ts` files with the \"typings\" field and preventing typing conflicts.\r\n\r\nI am not a believer in having \"version overrides\" for `.d.ts` files. First off, if I'm distributing typings in my packages, my packages are already versioned. My `.d.ts` files should not duplicate that information. Secondly, _it doesn't solve the problem_. If I include two packages which depend on ambient types from the same utility library, and that library has had a _breaking_ change in API between the included versions, _my project_ will fail to compile, despite not interacting with/caring about it at all. (This could be changed by not typechecking included modules but that's a slightly different but related issue.) Additionally, this artificial conflict doesn't indicate any runtime problems, since this configuration is perfectly valid in the node package model. This \"problem\" only exists in typescript's type-checker's flawed understanding of the packaged world. Packages have per-file scopes in node. External modules are capable of capturing that, which is why the problem does not arise when only using external modules. Our issue is one of reuse - we want to be able to reuse our ambient module declarations (which don't capture these scoping constraints) in package contexts while maintaining package semantics. I see the reliable way to do so is to _isolate_ ambient declarations to within their package's scopes. In this way, a package maintainer only has to manage typings around their own package - not their dependencies' dependencies typings, or their consumer's.\r\n\r\nIf we accept package scopes as a possible solution we come to the problems with implementing this solution. This requires some somewhat serious work within the type checker, from what I'm told. We don't actually have a concept of a package scope - we have a global scope and file/block scopes. External modules work because we rely on file scopes to isolate everything. To add package scopes, we'd need to add another scope concept between globals and files, and it would only exist by inference (if a package is accessed via an import statement which reads a typings field from a package.json, then inject all global files accessed via that file into a package scope for that package instead, and any external modules inside that package scope access that package scope before the global scope when looking up global types). This is nontrivial by comparison to the proposed `version` tag, which is why it's met with some resistance.\r\n\r\nUndoubtedly, the way forward is to mirror your source module JS files (which are external modules) with external module `.d.ts` files - this will never have problems - but is cumbersome and usually requires some nontrivial rewriting of existing `.d.ts` files. It also gives you the issue of needing to maintain _completely_ separate typings for the browser, where you want ambient module syntax, and for node."}}