Skip to content
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

Cannot import npm package with definition defined in typeroots #11137

Closed
jonaskello opened this issue Sep 25, 2016 · 10 comments
Closed

Cannot import npm package with definition defined in typeroots #11137

jonaskello opened this issue Sep 25, 2016 · 10 comments
Labels
Suggestion An idea for TypeScript Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@jonaskello
Copy link

jonaskello commented Sep 25, 2016

TypeScript Version: 2.0.3

I am trying to make a definition file for an existing npm package without using ambient definition such as declare module 'foo' {...}. In the example below I am trying to create typings for the npm package fibonacci.

Code

package.json:

{
  "name": "typings_test",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "fibonacci": "^1.6.1"
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "outDir": "js",
    "typeRoots": ["./types"]
  },
  "exclude": [
    "node_modules"
  ]
}

test.ts:

import * as fibonacci from 'fibonacci';

const bigNumber = fibonacci.iterate(3000);
console.log (bigNumber);

types/fibonacci/index.d.ts:

export function iterate(x:number);

Compile to see the error:

$ tsc
test.ts(1,28): error TS2307: Cannot find module 'fibonacci'

Expected behavior:

My expectation would be that tsc should pick up the definitions in types/fibonacci/index.d.ts because of "typeRoots": ["./types"] in tsconfig.json.

Actual behavior:

I get error TS2307: Cannot find module 'fibonacci'.. Using --traceResolution I can see that tsc is finding the file types/fibonacci/index.d.ts. If I move the file types/fibonacci/index.d.ts into node_modules/fibonacci/index.d.ts it works as expected.

@jonaskello jonaskello changed the title Cannot import existing npm package with types defined in typeroots Cannot import npm package with types defined in typeroots Sep 25, 2016
@jonaskello jonaskello changed the title Cannot import npm package with types defined in typeroots Cannot import npm package with definition defined in typeroots Sep 25, 2016
@chenzhutian
Copy link

chenzhutian commented Sep 25, 2016

According to the handbook I think you have to use ambient definition. I have a similar problem in #11136 , and the comments of the answer of this question help a lot

@jonaskello
Copy link
Author

@unhealthy I think the handbook has been updated for 2.0. If you look at the templates in the handbook for how you are supposed to write definition files, none of them uses ambient modules. Instead they say you should name the file index.d.ts and place it in a folder named like the library. To me this seem consistent with how the compiler option typerRoots work. Also looking in #7156 it seems like resolution of definition files should be able to work without ambient definitions. Could you point to the part of the handbook that states you should use ambient module definitions?

@jonaskello
Copy link
Author

jonaskello commented Sep 25, 2016

Also if I create a directory called node_modules/@types/fibonacci and move the index.d.ts file there it works. I thought the the typeRoots option should be able to provide more places to look besides node_modules/@types but not sure.

@chenzhutian
Copy link

In the chapter Module Resolution,

If that didn't work and if the module name is non-relative (and in the case of "moduleA", it is), then the compiler will attempt to locate an ambient module declaration. ...

So according to this document, if you use the default resolve strategy, which is Node strategy, typescript will always resolve the non-relative module in a path like root/**/node_modules/thepackagename. Then if the module has not been found, the compiler will attempt to resolve it as ambient module.

I have read the templates also and followed it to write declaration, and I met the same error as you.
I think the templates are guidance for writing declaration file to publish as a @types package, or for writing declaration file for the package of yourself, not for the third party existing npm package. For example, if you have written a package fibonacci, you should put your declaration file in fibonacci/index.d.ts. If you want to write declaration file to use existing fibonacci package, you should use ambient definition.

@jonaskello
Copy link
Author

@unhealthy Aha, I see now what you mean, I was looking in the declaration files section of the handbook. In the module resolution section there is no mention of @types, but in reality that is also looked at while resolving a module in 2.0 (You can see that with the --traceResolution compiler option). So I suspect that section is not yet updated for 2.0.

Also if I were to develop typings for a third-party package with the intention of later publishing them onto npm @types, they should not be ambient as you noted. Where should I put that definition file while working on it (before publishing to npm)? Putting it under node_modules/@types while working on it makes no sense to me so.

@chenzhutian
Copy link

Yes you're right. I have used --traceResolution and notice that compiler will also attempt to resolve module in '"/node_modules/@types/"'.
I also don't know where to put declaration file of the third party package and don't think it's a good idea to put them under '/node_modules/'.

@mhegazy mhegazy added Suggestion An idea for TypeScript Committed The team has roadmapped this issue labels Sep 26, 2016
@mhegazy mhegazy added this to the TypeScript 2.1 milestone Sep 26, 2016
@mhegazy mhegazy assigned ghost Sep 26, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Sep 26, 2016

typeRoots should be used in module resolution as a fall back option if we did not find the module in any other place.

@jonaskello
Copy link
Author

A good workaround as mentioned in #11329 is to have a folder, eg. custom_typings and under that folders with same name as modules which contains index.d.ts for each module. Then add this to tsconfig.json:

"compilerOptions": {
  "baseUrl": ".",
  "paths": {
     "*": ["custom_typings/*"]
  }
}

@mhegazy mhegazy modified the milestones: TypeScript 2.1, TypeScript 2.1.2 Oct 27, 2016
@ghost ghost added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Committed The team has roadmapped this issue labels Nov 1, 2016
@ghost
Copy link

ghost commented Nov 1, 2016

If you want to import things, you should paths as in the above example.

@ghost ghost closed this as completed Nov 1, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Nov 1, 2016

just to fill in some details for future searches. the resolution for module imports follows that of node (using --moduleResolution node). and that stipulates that local node_modules should be loaded first. typeRoots by definition are top-level to your project, so looking up modules in typeRoots would break dependencies. e.g.:

typeRoot
     packageA

node_modules 
    packageB
         node_modules
                [email protected]
   [email protected]

in pacakgeB you really want imports to resolve to [email protected] and not any other one. if this is changed to look somewhere else, dependencies start breaking and you can not possibly have multiple versions of the module in your project.

This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Suggestion An idea for TypeScript Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants