Update 5 November 2015
The functionality requested below is currently implemented in typescript since at least 1.8 with one main difference:
Instead of having typescript.main
and typescript.definition
properties, there is only one typings
property which you can point to either a d.ts
file or a normal .ts
file.
If you're developing a module to just use locally, you can have the typings
point to a .ts
file, but if you plan to publish the module, it is recommended to have it point to a d.ts
file. This is because you don't want your module consumers to recompile your module files, just consume its typings.
I have setup an example of using this here:
https://github.com/chanon/typescript_module_example
There is a documentation page here that has more information:
http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html
Thank you TypeScript devs and all contributors.
Original issue / feature request follows
In TypeScript it is a lot harder to re-use typescript modules compared to re-using npm modules in JavaScript.
It would be beneficial if the typescript compiler is smart enough to look in node_modules folders and package.json files.
The reason is so that npm module developers that use TypeScript might be able to start writing and distributing modules through npm itself. TypeScript would be able to piggyback on npm's infrastructure and wide support.
If we had:
./node_modules/concator/index.ts
./myApp.ts
And in index.ts we had:
export function concat(param1: string, param2:string): string {
return param1 + ' ' + param2;
}
in myApp.ts:
import concator = require('concator'); // loads the module from node_modules
var result = concator.concat('I like', 'this.');
var wontWork = concator.concat('this will fail'); // compile error, concat needs 2 params
So basically, the compiler is smart enough to find the module in node_modules and it automatically uses the typescript version (index.ts).
Then when the code is compiled to JavaScript, it naturally uses the JavaScript version.
A more basic case is supporting Node.js's popular rule of http://nodejs.org/api/modules.html#modules_folders_as_modules as suggested by @vvakame below and in the closed (semi-duplicate) #207 issue.
There could be an addition to package.json files to specify where the main .ts file of a TypeScript npm module is. This would be similar to the main
key that specifies where the main JavaScript / .js file is but for TypeScript instead.
Eg package.json for an npm module named "myModule" located at node_modules/myModule/package.json
{
"main": "./dist/index.js",
"typescript": {
"main": "./src/index.ts"
}
}
In this example node_modules/myModule/src/index.ts
would be the main TypeScript file and doing an import myModule = require("myModule");
would import the node_modules/myModule/src/index.ts
file.
For a JavaScript coder writing var myModule = require("myModule");
in a JavaScript file, the require would load the node_modules/myModule/dist/index.js
file as usual.
As can be seen in this example, the TypeScript src is in the node_modules/module-name/src
folder and the compiled JS files would be in node_modules/module-name/dist
.
Something like this could be a (semi) standard for TypeScript npm modules so that the TypeScript source is cleanly separated from the compiled JavaScript output.
A more basic case as suggested by @vvakame below is supporting the popular Node.js rule of http://nodejs.org/api/modules.html#modules_folders_as_module
Another possible key for package.json for non-TypeScript (plain JavaScript) npm modules could be typescript.definition
. This would point to a .d.ts file that defines the module for TypeScript users of the npm module.
So that an
import $ = require('jquery');
would automatically read a jquery.d.ts
file defined in the typescript.definition
key in jQuery's package.json and make $
the correct type.
Example format:
{
"main": "./dist/index.js",
"typescript": {
"definition": "./index.d.ts"
}
}
(This format is already used by tsd as @Bartvds explains below.)
Then us TypeScript coders would just have to try to get as many non-TypeScript npm module maintainers to merge our pull requests that have our .d.ts files and package.json typescript.definition
keys.
If we succeed with that, then TypeScript coders' life would be bliss ... no more separate managing of DefinitelyTyped .d.ts files. Just npm install and you get your TypeScript definitions too! Automatically and up-to-date with the module version installed.
What we get from all of this is
typescript.definition
key addition. This allows .d.ts definition files to be packaged together with the npm module so that updating an npm module will automatically update its .d.ts definition file. This removes the need to update .d.ts files manually.typescript.definition
is simpler because it is simply an import moduleName = require("moduleName")
statement with no need for a separate ///<reference ...
typescript.definition
should also allow the use of different versions of the module in the same code base without type names clashing.@Nemo157 has written a very detailed proposal for how all of this should work at:
Proposed TypeScript Require Resolution Semantics
https://gist.github.com/Nemo157/f20064a282ee620f3877
An addition in the proposal is the usage of /typings
folders which can hold definition files that can be automatically managed by tools such as tsd
for JavaScript npm modules that won't include definition files in their repositories.
Since TypeScript compiles to JavaScript which runs mainly in two places: node.js and in browsers, supporting node_modules is beneficial to both places (practically all of where TypeScript is used) because of npm and Browserify/Webpack.
ie. there is no reason to come up with a different scheme when node_modules is what all TypeScript users use already for maybe 75%-100% of all their JavaScript code. (Taking out 25% for maybe RequireJS users.)
[1] - BTW I see there is a NuGet package manager (?) from Microsoft that can distribute typescript modules (?), but coming from a node.js focused (non .NET focused) background, I don't see NuGet becoming widely used outside of Microsoft focused shops, especially as npm is _the_ standard for node.js and is a leading standard for client side JavaScript too. If I didn't use TypeScript I would have never heard of NuGet. The average node.js / client side JavaScript coder would probably prefer using npm, the same tool they use already rather than having to use something Microsoft specific such as NuGet. (I don't actually know a thing about NuGet so what I'm saying here might not actually matter.)
[moved to typescript.main in package.json in the issue description above]
:+1:
I think http://nodejs.org/api/modules.html#modules_folders_as_modules is very popular rule of Node.js.
other example.
./test/index.ts
export function hello() { return "Hello, world"; }
./main.ts
import test = require("./test/");
console.log(test.hello()); // print "Hello, world"
[moved to typescript.definition in package.json in the issue description above]
One solution would be to solve it with better tooling. E.g. import foo = require('foo')
gives you a hint to look for some local node_module+package.json (foo is a ts project) or DT definition (foo is a js project where library authors don't want to maintain the ts def).
FYI; I'm actually testing something similar to this in TSD; a way to expose & link definitions that are bundled in the npm (or bower) packages.
It's in my dev version for 0.6, and it works by adding a typescript
element to the package.json (or bower.json), with a sub element definition
(a sub element because maybe one day there'd be source
too, or whatever).
{
...
"main": "./index.js",
"typescript": {
"definition": "./foo.d.ts"
}
...
},
Then you can run a command on TSD, currently tsd link
and it will scan all package.json files in node_modules (or bower or whatever), find that property if defined and add a reference to it to the central tsd.d.ts
bundle in your project.
@Bartvds That's pretty nice. Could be the first step for having .d.ts in npm packages. I like the package.json structure with "definition" in "typescript", it's a lot more organized.
If TypeScript compiler itself could read it automatically, would be very cool.
Tagging this for discussion -- smarter external module resolution is something we need to talk about to understand the various scenarios here. This was brought up previously and we made a few tweaks in 1.0.
Also discuss #207 - looking under 'index'
:+1:
@chanon tsMain
is not necessary if we use declarations generation.
Yep, the issue is immensely important even for browsers - lots of projects use browserify/packify and therefore node-compatible directory layouts
:+1:
There's been some work already in this area back in the codeplex repo, one pull request by leebyron and one by kayahr.
I've been meaning to try and port one of them to the new repository, but it seems the related code has been re-arranged a lot.
I have written a proposal for how to add this to the TypeScript compiler. This also includes looking in the typings
directory as that will be important when working with javascript modules that will not maintain their own typescript definitions. Unfortunately to make this all work transparently with tsd
will still take a little work as the requirement of /// <reference
d files being ambient external declarations makes things a little hairy. I will take a look at implementing this on a branch and providing some example modules using it.
Looks sensible. Will this cover modules with clashing type dependencies? I mean if an app imports A and B, and B is also dependent on A. It's easy to fall into duplicate type errors if there are two copies of the external declarations.
It shouldn't, they should resolve to different external module names so declare identically named independent types in different modules. The structural type checking should then allow the different modules to interact without issues.
That's one of the major issues with the current tsd
definitions that this is trying to resolve, by defining external ambient modules there is no way for them to handle having multiple versions of a library with identically named, but slightly different types.
@joewood also there will be troubles between different versions of A
├── [email protected]
└── [email protected]
└── [email protected]
there is no way for them to handle having multiple versions of a library with identically named, but slightly different types.
interested to hear solutions to the same. TypeScript has a global
level variable scope for ambient declarations
Even if the versions match, at the moment I see issues with two different, but matching ambient declaration files causing duplicate symbol errors. The locally defined /// reference
directive needs to somehow resolve back to a single file. Either that, or the type identity needs to include the path and structural typing will take care of version mismatches etc..
@joewood Yep, that's what I hope will be fixed in the majority of cases by changing require
to load the correct definitions, avoiding /// reference
wherever possible. The external modules will be identified by their absolute paths instead of having ambient declarations, so loading multiple of the same name from different paths and at different versions should be possible.
Yep, that's what I hope will be fixed in the majority of cases by changing require to load the correct definitions, avoiding /// reference wherever possible.
:+1:
:+1: Thanks @Nemo157 and everyone for the discussion.
I've updated the top issue text to merge the package.json additions and linked to @Nemo157's proposal.
:+1:
I've done an initial implementation of my proposal along with creating an example set of modules to show that all the different ways to make a module will work. Including using different versions of the same library in different sub-modules.
:+1:
Folders as modules with index.ts :+1:
:+1:
:+1:
:+1:
:+1:
:+1: :+1: :+1:
:+1: :+1: :+1: :+1:
Commenting to bump this back onto our radar.
I've had good success with @Bartvds's suggestion :
Then you can run a command on TSD, currently tsd link and it will scan all package.json files in node_modules (or bower or whatever), find that property if defined and add a reference to it to the central tsd.d.ts bundle in your project.
So definitely consider supporting typescript
in package.json
as that's the way community has gone.
We definitely need tsc to be aware of the node_modules
folder if TypeScript is going to be widely used in the node.js community. I currently use symlinks to modules that are part my development project (using npm-workspace), and I have two options at the moment:
import Foo = require("node_modules/mymodule/Foo");
Could tsc check the external module name in import declarations and then during path resolution also apply node_modules
in the same manner that the node.js runtime does? A compiler option could also turn this on or off.
:+1: :+1: :+1:
Could tsc check the external module name in import declarations and then during path resolution also apply node_modules in the same manner that the node.js runtime does
This is how it _should_ be. The current manner of just looking up the directory tree to find a file with the said name bares no resemblance to JS execution contexts.
This has been on my list for a while now. will try to get this in the next release.
We (Booktrack) use TypeScript for all our web development, and we face similar challenges to the node.js developers. I speak up because the voice of web devs doesn't seem as loud as that of the node devs.
Solutions (in order of worst-for-us to best-for-us):
node_modules
node_modules
directoryOption 1 is terrible, I'd rather option 2.
Option 5 and 6 would allow for exotic uses of import statements, such as those found when working with requirejs or webpack plugins. The basic idea: the compiler delegates all external module name lookups (not just top-level) to a third-party (a plugin or callback). The delegate, given the path to the module under compilation and the external module name, returns to the compiler the filesystem path OR the type information for the given module name.
I'd be happy if option 4 was implemented, delighted if option 6 was implemented and over-the-moon if both option 4 and option 6 were implemented.
How about a --basePath for your module search root, all module imports will be looked relative to that path. This only applies for AMD.
I think the node issue is much simpler, we just need to implent it:)
Note that we actually got an implementation (and tests) from @Nemo157 here long ago https://github.com/Microsoft/TypeScript/issues/247#issuecomment-57422329
Agree with @mark-buer and @mhegazy, option 4 a search path should be a simple fix. A more elaborate solution in the language service (option 6) is definitely required longer term.
_Worth adding_: this alone won't fix the TypeScript reuse issue in npm because of the duplicate symbol issue for common type references #1125. We really need to avoid ///
Another vote for getting the node_modules folder recognised by the typescript compiler.
For CommonJS modules, typescript should ideally be following the same filename resolution that require.resolve does (as outlined by the psuedocode on this page: http://nodejs.org/api/modules.html#modules_all_together.
this functionality is critical to allow integration of the typescript compiler into other node packagers, such as browserify. @Nemo157 's patch does a fine job, but it doesn't allow for easy testing with existing packagers as most have moved on to later versions of typescript and are no longer compatible with his code
Where does the node_modules
directory have to be relative to the sources?
Atom-TypeScript now supports typescript.definition
out of the box. Details : https://github.com/Microsoft/TypeScript/issues/2829
You can also create such packages easily using atom-typescript :rose: again see #2829
It will work _flawlessly_ if you share a package that doesn't depend on any external libraries. If it does then we need a module conflict resolution algorithm.
I am open to suggestions for this case, but my plan is:
d.ts
not giving TypeScript the external reference
comments (e.g. node.d.ts here https://github.com/TypeStrong/atom-typescript-examples/blob/master/node/node_modules/example-typescript-b/definition/sample-b.d.ts) And instead pointing to our own .d.ts
if we have it in our Typings. Is this going to stay on track for 2.0? It seems like kind of an important feature for Node.js adoption, which right now is painful, even with DefinitelyTyped and the likes.
It seems like kind of an important feature for Node.js adoption, which right now is painful, even with DefinitelyTyped and the likes.
@LPGhatguy Please see https://github.com/Microsoft/TypeScript/issues/2338. @vladima is working on it but don't think its been assigned any milestone yet :rose:
@LPGhatguy we are trying to get this in the next release. hopefully soon.
@mhegazy You mean 1.6? That would be awesome!
Is that related to #2338?
yes for TypeScript 1.6 (at least this is what we are trying to do), an yes for #2338; there are a couple of other changes and issues around this around including #3147 and #4154
Then, why does it tagged as TypeScript 2.0 as a milestone?
thanks @heycalmdown, removed the milestone. we do not usually tag milestones to suggestions. hopefully we have an update within a week or so on this issue. stay tuned.
I would like to chime in asking for ES6 module support to follow this as well (which I assume it would?)
Typings for
import mylib = require('mylib');
mylib.foo(mylib.bar);
should behave the same as
import { foo, bar } from 'mylib';
foo(bar);
I would like to chime in asking for ES6 module support to follow this as well (which I assume it would?
@DavidSouther That would _automatically_ be the case. The lookup between the two is consistent :rose:
this is handled by #4154.
If I'm using nightly, starting tomorrow I should expect importing node modules to "just work"?
Question:
I think it is a common convention to have files named
"index" in commonJS and
"main" in AMD
for short paths.
In the question on top the author imports
/node_modules/concator/index.ts
as
import concator = require('concator');
Please forgive me - I am currently figuring out if also the index resolution is supported now and how about index.ts or main.ts in AMD requires` resolution?
I am also pinging @basarat to know if it is / will be supported by https://github.com/TypeStrong/atom-typescript because now it does not allow me to require any index.ts as mentioned above.
@sebilasse, if you are using typescript@next
today, this should work with --module commonjs
import concator = require('concator'); // resolves to node_modules/concator/index.ts
There are not changes to how AMD resolution from previous releases.
@DavidSouther it should be in typescript@next
today. give it a try and let us know how it fares.
@mhegazy @basarat :+1: ok. But how do you think about AMD resolution for main.ts or index.ts - shouldn't it be consistent in the future?
@sebilasse, for AMD i think we need to do something as noted by @vladima in #2338 (section for: RequireJS/ES6 module loader).
Very nice!! \o/
@mhegazy Still not working quite as I expect.
I put together https://github.com/DavidSouther/typescript-example-using-node with my "ideal" use case. tslib is a simple library that exports a single function. tsclient depends on tslib, as well as readline
from the Node packages. The setup script is a convenience to install, link, and execute. I've included a run, and annotated the unexpected portions with inline comments.
% ./setup
...
> [email protected] build ~/ts-node/tslib
> tsc --version ; tsc -p src/
message TS6029: Version 1.7.0-dev.20150831
...
> [email protected] build /Users/southerd/devel/tmp/ts-node/tsclient
> tsc --version ; tsc -p src/
message TS6029: Version 1.7.0-dev.20150831
# Expect this to find tslib, but fail type checking.
# See tsclient/app.ts for details
src/app.ts(4,21): error TS2307: Cannot find module 'tslib'.
+ node ./dist/app.js # This works as expected!
What would you ask? What is the meaning of life?
42
+ set +x
I am also not getting language support for tslib imports in tsclient (VSCode Version 0.7.0 (0.7.0)), though I am not entirely sure which TSC it's using, or how to change that.
@DavidSouther TypeScript compiler check 'typings' field in package.json to find '.d.ts' files. With this change I'm getting src/app.ts(13,7): error TS2322: Type 'string' is not assignable to type 'number'.
which looks like a legitimate error
Ah, I must have missed that piece of docs. I'm still having the second issue - how do I change the VSCode tsc version?
you can provide custom TypeScript SDK location to VSCode via "typescript.tsdk"
setting - it should point to the folder containing tsserver.js
and standard '.d.ts' files. In case if TypeScript is installed as npm package it will be something like
// Place your settings in this file to overwrite the default settings
{
"typescript.tsdk": "C:\\Sources\\bugs\\node\\typescript-example-using-node\\tslib\\node_modules\\typescript\\lib"
}
Did anyone get a sample working ? I tried the whole afternoon to play with this, but I always end up with
error TS2307: Cannot find module
At the moment the code is in node_modules/my-module/. And I defined an index.ts that takes care of exporting everything, and I referenced it in the package.json under typescript.main. In the consumer project I tried import {AClass} from 'my-module'; and import my-module = require('my-module'); They both lead to the same error with typescript 1.7.0-dev.20150901.
When resolving a module with non-relative name, with --module commonjs
, the compiler will look for a .d.ts
that matches the name in node_modules\name\index.d.ts
or look in package.json
for a property typings
, and load the .d.ts it points to.
from your description, looks like you are setting it to index.ts
, you do not really want to compile your dependencies, you just want to consume their typings, so you should set it to index.d.ts
instead.
I've updated steps in node module resolution algorithm to reflect the implementation: https://github.com/Microsoft/TypeScript/issues/2338
@DavidSouther I have the exact same problem apparently. When I compile with a dependency in node_modules it can't resolve the module and outputs an error, but then, it still generates the js, and if I run it, it runs. Maybe it's due to my setup I don't know. My d.ts is mostly empty, I have an index.ts that takes care of importing and reexporting all the classes of my modules defined in many .ts files. And then my d.ts file just references this index.ts and exports everything from index.ts like this :
/// <reference path="index.ts" />
declare module 'my_module' {
export * from 'index';
}
Also, it somehow still compiles the ts from node_modules, therefore I should add a clean task to delete it after compilation. Is there a description of the feature somewhere that sums up the process ?
edit: I got it to work, but it's super hacky and still outputs errors. I created a d.ts with dts-generator, and then I imported my classes like this :
import MyClass from '../../node_modules/my_module/dist/MyClass';
If I use import MyClass from 'my_module/MyClass' I get it to compile without error, but at runtime I get the error cannot find module 'my_module/MyClass'. With the above solution it points directly to the compiled .js and at runtime it works somehow, even though it outputs the error cannot find module at compile time.
Still having issues importing from submodules (eg npm install angular2, import { Inject, Binding } from 'angular2/di'
. Will work on getting a contained test case up tonight.
Don't think that angular already bundles its typings in the way expected by TypeScript compiler.
That's likely true; I'll work with them and see where it ends up.
this feature is working well on typescript 1.1 , for example https://github.com/Nemo157/typescript_w_node_modules_example
but fail in typescript 1.5.3 , anything changed ?
------ update ---------
I know it will release in 1.6 , thanks
I can only get this "half-working" in 1.6.2
First, I package a library with a my-lib.d.ts
in the dist
directory, and points to this file in the package.json
file typings
attribute (e.g. "typings" : "dist/my-lib.d.ts"
)
Then I import this library in a Test.ts
TypeScript file using
import { MyObject } from "my-lib"
MyObject
is properly imported and the js code is emitted on transpilation.
Visual Studio Code even provides completion on MyObject
However I get compiler warnings that:
Test.ts(10,60): error TS2306: File '[]/node_modules/my-lib/dist/my-lib.d.ts' is not a module.
Visual Studio Code will actually show the import as a hard error
Any imported modules from a node package need to be "external modules" and not "ambient module declarations". i.e. no declare module "foo" {.. }
declarations, but rather, top level import or export declarations in the file.
So if your package "my-lib" is written in typescript, just build it with --declarations
and set the typings to the .d.ts
file of your main module. if not, and your typings are something you hand authored, or got from definitely typed, then you will need to either change it to an external module, or wait until #4665 is resolved (hopefully soon).
The reason of this restriction is this can cause global scope pollution and conflicts later on for your package users. there is a long discussion about this in #4665.
Here is some documentation for future searches: https://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages
@mhegazy Thank you for your quick reply which really helped in my quest to automate the build of commonjs librairies, written in Typescript, which can be consumed by both Javascript and TypeScript code.
import/export
syntax and resolution has been moving so fast recently that what I guess is missing right now is a definitive guide on how to build one.
I got it working... with one caveat (and hence another question)
Here is a simplified view of the setup.
The library consists of three objects A1, A2 and B in 2 typescript files A.ts and B.ts; something like
sources
A.ts
class A1{}
class A2{}
export { A1, A2 }
B.ts
class B{}
export { B }
What we want to expose is collected in an index.ts
:
export * from './A'
export * from './B'
build
The build is conducted with the help of grunt and the --module commonjs
and --declaration
flags are set on tsc
1.6.2
The end result of the build is a tree that looks like
package.json
dist/
js/
A.js
B.js
index.js
typings/
A.d.ts
B.d.ts
index.d.ts
package.json
contains these two entries:
"main": "dist/js/index.js",
"typings": "dist/typings/index.d.ts"
Using this library in TypeScript 1.6.2 with a simple import {A1, A2, B} from "mylib"
works absolutely fine. Multiple level dependencies (i.e. librairies importing each other) works fine too.
Where problems arise is when the library depends on another library which is _not_ a TypeScript library.
Say the library depends on Node.js.
In one of the source files there will be a reference to the typings of NodeJS e.g.
///<reference path="../typings/node/node.d.ts"/>
Upon transpilation that <reference >
instruction is going to end up in the corresponding declaration file; the problem is that the path to node.d.ts
is likely to be wrong or inexistent.
Is there a recommended way to go about this ?
_Note_: this problem is alleviated by the use on an index.ts
file to expose the interesting parts of the library, since index.ts
has no reason to contain a <reference >
instruction and with 1.6.2, the compiler does not seem to care that A.d.ts has invalid path in the reference instruction
@bgrieder We handle this in Phosphor using tsconfig.json
:
https://github.com/phosphorjs/phosphor-widget/blob/master/src/tsconfig.json
We simply add any external typings we need to the compiled files. This does mean that if any of those external types are part of our public interface, the consumers of the code will also need to provide those external typings as part of their build. _This is okay_. If we were to bundle those definitions, and some other lib used by the consumer _also_ bundled those same definitions, there would be duplicate symbol conflicts.
@sccolbert Yes !
I was worried that removing <reference ...>
instructions would break auto-completion on all IDEs, but hey, no: at least Visual Studio Code 0.8.0 seems to be smart enough to pick those definitions from the tsconfig.json
file ! (after a restart)
Done with reference
. Excellent !
@sccolbert
What if you are using a plain Node.js
project within it and *.d.ts for it, how does the tsconfig
solution help you?
@heycalmdown you just add the d.ts
to the files
field on the tsconfig
, here's an example which does that:
https://github.com/phosphorjs/phosphor-widget/blob/master/test/src/tsconfig.json#L11
https://github.com/phosphorjs/phosphor-widget/blob/master/test/src/index.ts#L10
Note that we have to use require
here instead of import
only because of how the d.ts
file for expect.js is written internally.
Okay, I got it. Your repositories look like a kind of a good example.
Has this actually been implemented in TypeScript 1.6.2?
If it has, can somebody tell me what I'm doing wrong here?:
https://github.com/chanon/typescript_module_example
You probably want the es6 import syntax.
On Wed, Nov 4, 2015, 6:10 AM chanon [email protected] wrote:
Has this actually been implemented in TypeScript 1.6.2?
If it is, can somebody tell me what I'm doing wrong here?:
https://github.com/chanon/typescript_module_example—
Reply to this email directly or view it on GitHub
https://github.com/Microsoft/TypeScript/issues/247#issuecomment-153688004
.
I've just updated it to use the es6 import syntax and I still get the same errors (cannot find module).
@chanon the package.json
for node_modules
packages need to have a typings
key which points to a d.ts
file, _not_ a typescript
key pointing .ts
files like you have now.
We use node modules resolution in PhosphorJS exclusively (on TS 1.6.2) and it works fine. Here's an example: https://github.com/phosphorjs/phosphor-widget/blob/f908341cb1d46ada8ad8149e695a75e7ea2fde57/package.json#L6
Thank you @sccolbert, however my initial proposal (at the top of this issue) was to allow for a typescript.main
property in package.json
which would point to the main TypeScript entry point for node module packages written in TypeScript.
This would allow for importing TypeScript modules in TypeScript code without the need for d.ts
typing files (there would be no need to even generate them).
@chanon I was just pointing out what you need to do to get your code running.
@sccolbert I see thanks.
Can someone who knows tell me if I should create another issue requesting this specific feature?
Main related issues to module resolution logic (other than this one) seem to be #2338 which is closed and #5039 which seems to be about supporting SystemJS style path resolution which looks very complex.
However just simple CommonJS style importing like initially requested in this issue seems to have been forgotten? At least the part about typescript.main and folders as modules?
I don't understand the need of having (and desire to have) d.ts files if both the module and the module consumer are already written in TypeScript.
It's actually not much different from importing a TypeScript file relatively ex.
Instead of having to do:
import * as lib from '../relative/path/to/typescriptFile.ts'
Or:
import * as lib from '../../node_modules/myModule/index.ts'
Just let TSC be able to find the typescript file to import using normal node_modules path resolution. And at least allow for importing folders as modules (with an index.ts) so that in the second example I could do:
import * as lib from 'myModule'
Or is it because of "you do not really want to compile your dependencies, you just want to consume their typings"?
@chanon the behavior you outline is what is in master now. can you give typescript@next
a try?
the original implementation of #2338 added some extra checks to ensure it is a .d.ts, and that is mainly for the reason you mentioned. you do not want your package users to compile your "sources" on every compiler invocation, and get different output based on their compiler options, you only want to share your typings.
However if you are building both packages you might want to do that while iterating on them. The restriction was removed in #5278.
Please take a look at the wiki page for sharing typings through npm package for more information: https://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages
Thank you for your clarification @mhegazy! I'll try it out.
@mhegazy Just tried with typescript@next
, works as expected!
This is great!! Thank you!!!
And thank you everyone for participating in this and related issues :+1:
I've added an update to the issue text above to explain the way this feature has actually been implemented.
At risk of stepping into a minefield, how is this expected to work with plain-old npm modules that _don't_ have a typings
property in package.json? Trying to use either tape
or get-parameter-names
with typescript@next
causes it to blow up in my face as it is unable to find those packages due to the fact they don't have that property.
Sure, they are not typescript modules, but I can't seem to find a work around and that locks out a significant portion of the npm ecosystem.
@danpantry Usually you would use a normal .d.ts file written specifically for that module. The DefinitelyTyped project has .d.ts files for lots of common modules. There is a tsd utility that can help install and manage .d.ts files from DefinitelyTyped for you.
You can use normal modules without typing by just using the normal commonjs require syntax.
eg
var moduleName = require("moduleName")
@chanon feels like a bit of a hack to use the 'classic' require
syntax when using the ES6 syntax with TypeScript so I can use JavaScript dependencies, but thank you. I have heard of the tsd utility but I am unsure of how that would factor in to using ES6 imports.. unless I have to start using /// <reference path=...
for these sorts of modules?
Yes that is one way. Another way is to us tsconfig.json files and put your root tsd.d.ts file as the first file.
You may also be interested in reading this, covering tsd and typings https://angularclass.com/the-state-of-typescript-packages/
@joewood thanks for the info, last time I looked into typescript was in the days of individual .d.ts
files. excellent article
@chanon @danpantry It's somewhat unfortunate that import foo = require('foo')
(with foo under node_modules) does not work when foo's package.json does not have a typings.
@wmono well, since maybe TypeScript 0.80 or maybe earlier import foo = require('foo')
syntax was only for importing modules with typings. If it didn't have typings you'd use var foo = require('foo')
instead. So I'd say its just the way it is.
@chanon Pardon me; user error. It looked like webpack was struggling with the "bare" require
but the problem lay elsewhere.
Sorry can someone clarify why
var foo = require('foo');
Works for a standard node_module without typings but
import foo from 'foo';
doesn't? Was that just arbitrarily defined? I don't see why the latter _shouldn't_ work - I don't care if the external JS library has typings or not.
Sorry can someone clarify why
var foo = require('foo');
Works for a standard node_module without typings but
import foo from 'foo';
doesn't? Was that just arbitrarily defined? I don't see why the latter shouldn't work - I don't care if the external JS library has typings or not.
@harangue because var require
was one of the competing module syntaxes (this one being commonjs
and others in the list include amd
) that was supported _outside of the type checking system_. If one wants to use the type system one would do import require
. With ES6 taking the :hammer: and saying that one syntax (:ring:) to rule them all ... it makes sense to have that module syntax supported by the type checking system out of the box :rose:
@basarat Thanks for the clarification. The icons were helpful as well. ;) I'm still not sure though why the ES6 syntax mandates type checking. What's to stop the compiler from saying - "oh, I couldn't find any type data for that module but I'll import it anyway". What side effects would this (rather intuitive, IMO) behavior incur?
This is confusing for those (like myself) coming from ES6 to TypeScript. I'd expect the ES6 module import syntax to behave the same way in TypeScript, but alas it doesn't work at all for most npm packages...
but alas it doesn't work at all for most npm packages...
@teohhanhui I'm confused. Can you give a concrete example of what you are trying to achieve and what code you tried to achieve that. This is just so that I may be able to help you :rose:
import koa from 'koa';
gives Cannot find module 'koa'. (2307)
when target
is es6
koa
is (of course) in node_modules
However, koa does not publish Typescript typings with their project. This is only for packages which publish a .d.ts with their project, and list it in their package.json. Because koa does not, you will want to additionally install those type definitions from Definitely Typed.
That seems like a poor decision as one would expect the import
statement to just work like it does in plain ES6 modules. (Yes I know, most of the npm
packages are CommonJS modules but supporting them here would still be the sensible thing to do.)
I'm new to TypeScript so I probably have the wrong understanding, but aren't type definitions optional? Why can't I import the package without the type definition? I don't want to be forced to look for / create type definitions.
Switching to the legacy TS module import syntax is not an option because it's disallowed when using the es6
target.
I'm new to TypeScript so I probably have the wrong understanding, but aren't type definitions optional?
That is incorrect. Type definitions _in your own code_ can be implicit, which means the Typescript compiler will "figure it out". You are never going to pass a third-party library through the Typescript compiler, so the compiler never knows what those types are. If you plan on using third-party code, you really should learn how to use Definitely Typed and the tsd tool.
What about this answer by @basarat?
Why can't non-TypeScript modules without type definitions be imported as such?
They can: const koa = require(‘koa’)
On 21 Jan 2016, at 14:47, Teoh Han Hui [email protected] wrote:
Why can't modules without type definitions be imported as such?
—
Reply to this email directly or view it on GitHub https://github.com/Microsoft/TypeScript/issues/247#issuecomment-173574234.
@bgrieder I'm referring to the ES6 import syntax.
(that is is you are using commonjs), if not, use the declare var
of Basarat answer
On 21 Jan 2016, at 14:48, Bruno Grieder bruno.[email protected] wrote:
They can:
const koa = require(‘koa’)
On 21 Jan 2016, at 14:47, Teoh Han Hui <[email protected] notifications@github.com> wrote:
Why can't modules without type definitions be imported as such?
—
Reply to this email directly or view it on GitHub https://github.com/Microsoft/TypeScript/issues/247#issuecomment-173574234.
I think the point being made is that one of TypeScript's goals is that valid JS should be valid TS. For the ES6 module syntax, TS should compile this with or without the presence of type declarations. Without type declarations an import
statement should just resolve to any
.
I totally agree with that.
@Joe Wood 👍
On 21 Jan 2016, at 14:52, Joe Wood [email protected] wrote:
I think the point being made is that one of TypeScript's goals is that valid JS should be valid TS. For the ES6 module syntax, TS should compile this with or without the presence of type declarations. Without type declarations an import statement should just resolve to any.
I totally agree with that.
—
Reply to this email directly or view it on GitHub https://github.com/Microsoft/TypeScript/issues/247#issuecomment-173575301.
FWIW this behaviour (expecting all 3rd party libraries to be typed ahead of time) is what is causing me to use Flow over TypeScript. While TypeScript has IDE support, expecting everything to be typed ahead of time isn't really reasonable, especially when there are plenty of libraries out there that:
./typings
folderI'd rather not have to fight something that's meant to be aiding my development.
@danpantry, why not just declare your lib entry point as any, and the compiler will be content with that:
declare var $: any;
That's a workaround. But I think it would be a good idea for TypeScript to respect the ES6 module code and fallback gracefully. I can't see a good reason why an import statement can't fallback to any
rather than reporting an error if the top level module is untyped.
I'd prefer the compiler to fail early and loudly (the current behavior), rather than allow runtime issues to creep in unnoticed. I'd prefer to explicitly have a declare var foo: any
line to indicate "this is unsafe, and I know what I'm doing".
Why not just allow the ES6 import syntax when no typings are available an issue a simple compiler warning that the import has resolved to any
, instead of an « emit preventing » error ?
On 21 Jan 2016, at 18:42, S. Chris Colbert [email protected] wrote:
I'd prefer the compiler to fail early and loudly (the current behavior), rather to allow runtime issues to creep in unnoticed. I'd prefer to explicitly have a declare var foo: any line to indicate "this is unsafe, and I know what I'm doing".
—
Reply to this email directly or view it on GitHub https://github.com/Microsoft/TypeScript/issues/247#issuecomment-173649845.
In any case, the compiler currently claims that it can't find the module, when in actual fact it's just refusing to import the module without type definition. That's not really helpful, is it?
^^^ This a thousand times. There was no indication that I was doing something wrong when I first ran into this issue. It took some long hard Googling to finally figure out what was up (incidentally I think this issue is one of the only findable places that documents it). It's so unnecessary. And with Angular 2 bringing TypeScript more into the mainstream I can only imagine how many Stack Overflow questions will come from this behavior.
Very much agree with the poor error handling description and just make it import as any!
@sccolbert I agree there should be a notification but I'm not sure it should be a failure - a warning instead, perhap
I agree with @mhegazy and @sccolbert.
I'd much prefer our production build scripts (i.e. CI server) to complain loudly if something is amiss.
Hi guys,
This behavior just drives me crazy. Definition files are not up to date or not registered into package.json
.
In the end TypeScript
produces JavaScript
, so, until we all go to this language pls be gentle to the rest of the world that provides libraries for the mother tongue in any other transpiled languages, like yours.
I really want to know if TypeScript
wants to stay (I would say "to integrate" as it looks is not there, yet) in the JavaScript
ecosystem or wants to live a life apart, as the second option will make me to go to something else.
To achive that a switch in the .tsconfig
file will do, for that we can say that we want strict imports or not.
As for CI loud complaint there are test frameworks for that in JavaScript
. Anyway, you'll not get type checking at runtime, the poor import checking is the less important issue to have.
All the best.
@devel-pa, the errors are meant to tell you that the compiler has no idea what your import is. any subsequent use of the import can not be checked.
Switching off the errors does not solve any issues. it just pushes these "unknowns" silently throughout your system. without type information, the compiler can not warn you on what is safe and what is not. and that is the whole point of TypeScript :)
As for generated JS. none of the TS type errors stop your output from being generated. if you do not care about the type errors, just ignore them all. the compiler still generates your matching .js files.
The solution for missing definitions is to declare them. you do not have to have the full shape of the module declared, but just the name. This allows the compiler to know that there is a module "myLibrary", it can warn you for typos in module names, and more importantly, no other modules would go unchecked.
declare module "myLibrary" {
var a: any;
export = a;
}
as outlined in #6615, TypeScript should support a shorter form of this soon.
I think the issue is pain this causes for onboarding projects. I think this problem is analogous to implicit any. Right now these imports are essentially implicit void. Sure, the errors can be ignored, but that causes a lot of noise and goes against the philosophy of gradual typing and valid JS is valid TS.
I can understand the confusion here for people new to TS. If they've actively used ES6 module syntax in JS, why doesn't it just work in TS? What's special about import from
over var x = require
- which _is_ treated as an implicit any. Originally import was a TS specific keyword, so it implied that the developer wanted the module to be treated as a typed module. Now that it's not exclusive to TS I don't think that assumption can be made.
implicit-any errors are on variable declarations without a type. but using an undeclared variable is still an error today. For jquery, you still need declare var $;
somewhere for the compiler to know that you really meant to have a global variable called $
and not just miss-typed it. it is more or less the same for modules, the compiler needs to know that there is a module called "myLibrary"
and that is not a miss-typed name. so just add a declaration for it.
on any rate, #6615 should support adding declare module "*";
to match all modules in your system, though i think if you do that, you are not getting any help from your tooling, and you are setting yourself up for a painful transition later on when you decide to remove it.
@mhegazy I understand your reasoning but I really have a problem with this rationale
Basically, you are telling us to design a "placeholder" module definition, and the problem will go away.
This risk is that after a while, on a reasonably sized project, these "placeholder" definitions may just creep up and will be buried in some typings directory. Having no more warnings, everybody will just forget about them and when an issue arises, going through every module definition to see which is a placeholder, is just going to be a pain.
The situation is even worse with typed modules (the ones with the typings
entry in package.json) that we would assume to be properly typed could actually be using those placeholders internally.
I understand these placeholders cannot be prevented, but I would much prefer that, at least, they are not encouraged.
What should be advocated is that by default, the import resolves to any
and that the compiler issues a warning on every compilation. So at least, looking a the compiler/CI logs, we know something is potentially fishy and needs to be improved.
(as a side not, as stated on the typescriptlang.org home page, Typescript is a 'superset' of Javascript, and IMHO any valid ES6 syntax should just be swallowed as is)
Workarounds and hiding the garbage under the carpet are for me the worst that can happen (beside ES6 super).
I am working with JS libs all the time, and usually with last versions as part of my job and extra coding at home. 90% are not typed or have old defs, 9% are not very well typed (as the compiler doesn't know to make one def for all the files). Neither mine have very good defs, for the same previous reason and for the reason that my target is JS, I don't think I have to care about the original language.
Also, I've seen the reason that there are 'unknown' libs. No, not at all, if the developers doesn't know and understand what libs are used, why bother with them, I know why I'm using stuff and why I'm adding to the stack. Warning and tests (in case it exists) are enough for that.
Pls, JavaScript first, this is the target and the ecosystem. TS is just an accessory for better coding and type check at compile time, but only for what we are building. The rest is none of typescripts business.
I want to mention that I was against nmps peerDependencies
too, I am the one who chose, not the software. Imperative programming, baby.
what about importing modules from JSPM directory instead of node_modules?
edit: i've found an existing ticket -> https://github.com/Microsoft/TypeScript/issues/6012
I just ran into an issue with Node module resolution, when using SystemJS to load a directory:
Apparently, while Node (and therefore TypeScript) understands that the path is actually a directory and therefore loads index.ts
instead, SystemJS does no such thing, meaning that valid TS code will actually fail to load in the browser.
This is also true of node modules that have a index.ts/js entry point or even use the package.json main
property. This is slightly easier to work with, because it is easy (although repetitive) to add a package configuration to SystemJS. This is not so easy when working with arbitrary directories.
What is the appropriate solution for this?
JSPM automatic module resolution is not currently supported. this is tracked by https://github.com/Microsoft/TypeScript/issues/6012.
The recommendation is to use path mapping module resolution support, see https://github.com/Microsoft/TypeScript-Handbook/blob/release-2.0/pages/Module%20Resolution.md#path-mapping
The above instructions are a start, but some people may need to do something additional... You may have to add
<Folder Include="node_modules" />
to .njsproj
I was building with gulp, and had TypeScriptCompileBlocked in .nsproj. To fix issues in debugging breakpoints in VS 15 Preview 5 (I was getting "frame not in module" error) and issues I was having with intellisense I had to add
Beyond both debugging and intellisense working, I also stopped seeing intellisense errors beyond the same exact errors I saw from gulp while it built, which is expected.
Most helpful comment
In any case, the compiler currently claims that it can't find the module, when in actual fact it's just refusing to import the module without type definition. That's not really helpful, is it?