After trying suggestions posted in other places, I find myself unable to get a typescript project running that uses an untyped NPM module. Below is a minimal example and the steps that I tried.
For this minimal example, we will pretend that lodash
does not have existing type definitions. As such, we will ignore the package @types/lodash
and try to manually add its typings file lodash.d.ts
to our project.
Folder structure
- node_modules
- lodash
- src
- foo.ts
- typings
- custom
- lodash.d.ts
- global
- index.d.ts
- custom
- package.json
- tsconfig.json
- typings.json
Next, the files.
File foo.ts
///<reference path="../typings/custom/lodash.d.ts" />
import * as lodash from 'lodash';
console.log('Weeee');
File lodash.d.ts
is copied directly from the original @types/lodash
package.
File index.d.ts
/// <reference path="custom/lodash.d.ts" />
/// <reference path="globals/lodash/index.d.ts" />
File package.json
{
"name": "ts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"typings": "./typings/index.d.ts",
"dependencies": {
"lodash": "^4.16.4"
},
"author": "",
"license": "ISC"
}
File tsconfig.json
{
"compilerOptions": {
"target": "ES6",
"jsx": "react",
"module": "commonjs",
"sourceMap": true,
"noImplicitAny": true,
"experimentalDecorators": true,
"typeRoots" : ["./typings"],
"types": ["lodash"]
},
"include": [
"typings/**/*",
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
File typings.json
{
"name": "TestName",
"version": false,
"globalDependencies": {
"lodash": "file:typings/custom/lodash.d.ts"
}
}
As you can see, I have tried many different ways of importing typings:
- By directly importing it in
foo.ts
- By a
typings
property inpackage.json
- By using
typeRoots
intsconfig.json
with a filetypings/index.d.ts
- By using an explicit
types
intsconfig.json
- By including the
types
directory intsconfig.json
- By making a custom
typings.json
file and runningtypings install
Yet, when I run Typescript:
E:\temp\ts>tsc
error TS2688: Cannot find type definition file for 'lodash'.
What am I doing wrong?
Unfortunately these things are not currently very well documented, but even if you were able to get it working, let's go over your configuration so that you understand what each part is doing and how it relates to how typescript processes and loads typings.
First let's go over the error you are receiving:
This error is actually not coming from your imports or references or your attempt to use lodash anywhere in your ts files. Rather it is coming from a misunderstanding of how to use the
typeRoots
andtypes
properties, so let's go into a little more detail on those.The thing about
typeRoots:[]
andtypes:[]
properties is that they are NOT general-purpose ways to load arbitrary declaration (*.d.ts
) files.These two properties are directly related to the new TS 2.0 feature which allows packaging and loading typing declarations from NPM packages.
This is very important to understand, that these work only with folders in NPM format (i.e. a folder containing a package.json or index.d.ts).
The default for
typeRoots
is:By default this means that typescript will go into the
node_modules/@types
folder and try to load every sub-folder it finds there as a npm package.It is important to understand that this will fail if a folder does not have an npm package-like structure.
This is what is happening in your case, and the source of your initial error.
You have switched typeRoot to be:
This means that typescript will now scan the
./typings
folder for subfolders and try to load each subfolder it finds as an npm module.So let's pretend you just had
typeRoots
setup to point to./typings
but did not yet have anytypes:[]
property setup. You would likely see these errors:This is because
tsc
is scanning your./typings
folder and finding the sub-folderscustom
andglobal
. It then is trying to interpret these as npm package-type typing, but there is noindex.d.ts
orpackage.json
in these folders and so you get the error.Now let's talk a bit about the
types: ['lodash']
property you are setting. What does this do? By default, typescript will load all sub-folders it finds within yourtypeRoots
. If you specify atypes:
property it will only load those specific sub-folders.In your case you are telling it to load the
./typings/lodash
folder but it doesn't exist. This is why you get:So let's summarize what we've learned. Typescript 2.0 introduced
typeRoots
andtypes
for loading declaration files packaged in npm packages. If you have custom typings or single loosed.ts
files that are not contained withing a folder following npm package conventions, then these two new properties are not what you want to use. Typescript 2.0 does not really change how these would be consumed. You just have to include these files in your compilation context in one of the many standard ways:Directly including it in a
.ts
file:///<reference path="../typings/custom/lodash.d.ts" />
Including
./typings/custom/lodash.d.ts
in yourfiles: []
property.Including
./typings/index.d.ts
in yourfiles: []
property (which then recursively includes the other typings.Adding
./typings/**
to yourincludes:
Hopefully, based on this discussion you'll be able to tell why the changes you mad to your
tsconfig.json
made things work again.EDIT:
One thing that I forgot to mention is that
typeRoots
andtypes
property are really only useful for the automatic loading of global declarations.For example if you
And you are using the default tsconfig, then that jquery types package will be loaded automatically and
$
will be available throughout all your scripts wihtout having to do any further///<reference/>
orimport
The
typeRoots:[]
property is meant to add additional locations from where type packages will be loaded frrom automatically.The
types:[]
property's primary use-case is to disable the automatic loading behavior (by setting it to an empty array), and then only listing specific types you want to include globally.The other way to load type packages from the various
typeRoots
is to use the new///<reference types="jquery" />
directive. Notice thetypes
instead ofpath
. Again, this is only useful for global declaration files, typically ones that don't doimport/export
.Now, here's one of the things that causes confusion with
typeRoots
. Remember, I said thattypeRoots
is about the global inclusion of modules. But@types/folder
is also involved in standard module-resolution (regardless of yourtypeRoots
setting).Specifically, explicitly importing modules always bypasses all
includes
,excludes
,files
,typeRoots
andtypes
options. So when you do:All the above mentioned properties are completely ignored. The relevant properties during module resolution are
baseUrl
,paths
, andmoduleResolution
.Basically, when using
node
module resolution, it will start searching for a file namemy-module.ts
,my-module.tsx
,my-module.d.ts
starting at the folder pointed to by yourbaseUrl
configuration.If it doesn't find the file, then it will look for a folder named
my-module
and then search for apackage.json
with atypings
property, if there ispackage.json
or notypings
property inside telling it which file to load it will then search forindex.ts/tsx/d.ts
within that folder.If that's still not successful it will search for these same things in the
node_modules
folder starting at yourbaseUrl/node_modules
.In addition, if it doesn't find these, it will search
baseUrl/node_modules/@types
for all the same things.If it still didn't find anything it will start going to the parent directory and search
node_modules
andnode_modules/@types
there. It will keep going up the directories until it reaches the root of your file system (even getting node-modules outside your project).One thing I want to emphasize is that module resolution completely ignores any
typeRoots
you set. So if you configuredtypeRoots: ["./my-types"]
, this will not get searched during explicit module resolution. It only serves as a folder where you can put global definition files you want to make available to the whole application without further need to import or reference.Lastly, you can override the module behavior with path mappings (i.e. the
paths
property). So for example, I mentioned that any customtypeRoots
is not consulted when trying to resolve a module. But if you liked you can make this behavior happen as so:What this does is for all imports that match the left-hand side, try modifying the import as in the right side before trying to include it (the
*
on the right hand side represents your initial import string. For example if you import:It would first try the import as if you had written:
And then if it didn't find it would try again wihtout the prefix (second item in the array is just
*
which means the initial import.So this way you can add additional folders to search for custom declaration files or even custom
.ts
modules that you want to be able toimport
.You can also create custom mappings for specific modules:
This would let you do
But then read those types from
some/custom/folder/location/my-awesome-types-file.d.ts
Edit: out of date. Read the answer above.
I still don't understand this, but I found a solution. Use the following
tsconfig.json
:Remove
typings.json
and everything under foldertypings
exceptlodash.d.ts
. Also remove all the///...
references