TypeScript Version: 3.4.0-dev.20190326
Search Terms:
scoped package local definition
Code
tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"noEmit": true,
"typeRoots": [ "./node_modules/@types", "./types" ]
}
}
source/index.ts
import Foo from '@scope/scoped'
new Foo()
types/scope__scoped/index.d.ts
declare class Foo { }
export default Foo;
source/index.ts:1:17 - error TS2307: Cannot find module '@scope/scoped'.
1 import Foo from '@scope/scoped'
~~~~~~~~~~~~~~~
Found 1 error.
types/scope__scoped/index.d.ts to node_modules/@types/scope__scoped/index.d.tsnode_modules/@types/scope__scoped/index.d.ts to types/@scope/scoped/index.d.ts
error TS2688: Cannot find type definition file for '@scope'.
error TS2688: Cannot find type definition file for 'scope__scoped'.
Found 2 errors.
Expected behavior:
A module located in any directory referenced by typeRoots compiler option behaves the same, whether it is in node_modules/@types or some other type root.
Actual behavior:
When the module is in node_modules/@types it can be resolved via the magic rename of scope__scoped to @scope/scoped. When the module is in any other typeRoot directory, it cannot be located.
Playground Link:
Related Issues:
Directly related to #15204 (but that is locked).
Related to #23999 as well.
I am aware that I can resolve this specific issue by wrapping my definition file in a declare module "@scope/scoped" { ... }. This issue is more for tracking the fact that there is a discrepancy that is unexpected from a user's point of view. I was writing this definition in preparation for uploading to DefinitelyTyped, and to get started I copied an existing scoped module from DefinitelyTyped into my project and ran into this problem (which took me quite a while to troubleshoot/isolate since I didn't expect node_modules/@types to be special/magic).
typeRoots does not impact the resolution of modules - it only changes which type definitions are automatically brought into the program (if types is unspecified), or provides additional places to look up type libraries specified from types or reference directives.
There is special logic when resolving modules that node_modules/@types is a fallback location if the normal logic doesn't work. There isn't currently a way to customize that fallback location or add additional places.
You might be able to make something with path mapping happen, but I haven't tried.
Would it be a reasonable feature request to have all typeRoots be eligible as a fallback for missing module resolution?
As a user, my expectation is that node_modules/@types is just a default location for type definitions and anything that works there would work in any typeRoot. Is there something that would break if this expectation held?
I often learn how to create type definitions by looking at DefinitelyTyped, and when something that works there doesn't work in my local project I'm left in a situation where I need to find another source of example to work from.
In the meantime, is there any workaround that would allow me to define a scoped package locally other than via the declare module "..." {} mechanic?
We can always have more options, but typeRoots already means something and the thing it means is not something to do with module resolution. Making it conflate with other things would increase confusion and complexity.
Path mapping is generally the right way to specify a different file location for a non-relative module name.
Something that is tickling my brain about your explanation is that if the module is not scope (and thus I can name the folder exactly), the definition works when I copy it into any typeRoots folder.
I think ultimately that is why this _feels_ like a bug, because everything works fine right up until I scope the package, then it all breaks.
Why is it that module resolution works in typeRoots when the name is an exact match, but it doesn't work when the module is scoped (and thus has to go through the name transformation during lookup)?
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
Just ran into this issue, and yes it does feel like a bug.
The above declared workaround declare module '@scope/scoped' { ... } does not work for me for some reason.
Now I don't know what to do.... Will look into manual type mapping.
May be it's folly, but my problem was in package.json. Yarn doesn't add scoped(! only scoped) packages automatically in dependencies block...
In included package:
tsconfig.json:
{
"compilerOptions": {
...
"declaration": true
...
}
}
package.json:
{
...
"types": "dist/index.d.ts"
...
}
So this was my solution that worked:
tsconfig.json:
{
"compilerOptions": {
"baseUrl": "./",
// add all extra @types declarations here (including non-scoped now)
"paths": {
"@abandonware/noble": ["./@types/abandonware__noble/index.d.ts"],
"json-diff": ["./@types/json-diff/index.d.ts"],
...
},
...
}
I tried ./@types/abandonware__noble/index.d.ts and ./@types/abandonware__noble.d.ts without any extra custom tsconfig.json options, but that did not work. ./@types/json-diff/index.d.ts worked without any extra config, but since adding custom compilerOptions I have to add that as well.
Is my solution a workaround, or is this the supposed way to solve adding type declarations for scoped packages?
TS Version: 3.7.4
I also tried ./@types/@abandonware/noble/index.d.ts but not working.
Did my solution in https://github.com/microsoft/TypeScript/issues/30599#issuecomment-590246047 work @TrueWill?
@johanlunds No, but thank you for posting that and for following up.
I tried:
typeRoots)"paths": { "@scope/package": ["typings/scope__package/index.d.ts"] }/// <reference types="typings/scope__package/index.d.ts" />./ or not)All without success. I finally gave up and just require'd the module.
To be clear, I was running into this with ts-node, so it may be specific to that tool. I'll delete my earlier comment.