Search code examples
node.jstypescriptmicroservicesnode-modulestsconfig

Typescript typeRoots not detecting the type definitions


I'm using TypeScript inside my project. If I try to import a package using commonJS

const { v4: uuidv4 } = require('uuid');

Then I don't get any error during compilation, but when I convert it into ES6 module like

import { v4 as uuidv4 } from 'uuid'; 

I get a compilation error like:

Could not find a declaration file for module 'uuid'. 'C:/Users/project/dependencies/nodejs/node_modules/uuid/dist/index.js' implicitly has an 'any' type. Try 'npm i --save-dev @types/uuid' if it exists or add a new declaration (.d.ts) file containing 'declare module 'uuid';

I already installed the @types/uuid.

I do face this issue with packages that need type-definition (@types/*)

If the project structure is something like the below example:

├── dependencies
│   └── nodejs
│       ├── package.json
│       ├── node_modules
│       └── package-lock.json
├── src
│   └── index.ts
├── tsconfig.json
└── .gitignore

To regenerate this issue, run the commands mentioned below:

$ mkdir project
$ cd project
$ touch tsconfig.json

add following json to tsconfig.json file:

{
  "compilerOptions": {
    "target": "es2017",                       
    "module": "commonjs",                     
    "outDir": "./dist",
    "strict": true,
    "noImplicitAny": true,
        "baseUrl": "./dependencies/nodejs/node_modules",
    "typeRoots": ["./dependencies/nodejs/node_modules/@types"],
    "esModuleInterop": true,
    "inlineSourceMap": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["dependencies", "**/*.spec.ts"]
}
$ mkdir dependencies/nodejs
$ mkdir src/models
$ cd dependencies/nodejs
$ npm init
$ npm i uuid
$ npm i -D @types/uuid typescript
$ cd ..
$ cd ..
$ cd src/models
$ touch index.ts
$ nano index.ts
import { v4 as uuid } from 'uuid'; // inline typescript error (same mentioned in the issue)
console.log(uuid()); 

uuid does support ESM modules and I did create an issue on uuid github. I finally found out that if package.json exists in the root of the project, I will never have to deal with such an issue.

Example:

├── src
│   └── index.ts
├── dist (or build
├── node_modules
├── package.json
├── package-lock.json 
└── .gitignore

I only face the issue with packages that need type-definition (@types/*) when use nested structured project (where package.json file isn't available in the root directory).

The project structure is based on using AWS Lambda with layers for serverless applications. And this "dependencies" folder is basically a layer and AWS-lambda layers are defined with such a structure as

dependencies/nodejs/package.json

Therefore, package.json should not be placed at the root of the project.

However, I believe this is not a package issue but rather a typescript TSconfig issue.

I tried to define typeRoots in tsconfig file but its not working.

Here is the github repository that contain sample project that generates the issue:

https://github.com/sulemanelahi/typescript-type-definition

I want to use ES6 Modules for importing uuid in nested structure project same as I shared.


Solution

  • I was able to fix the issue by adding paths property:

    "baseUrl": "./dependencies/nodejs/node_modules",    
    "paths": {
      "uuid": ["./@types/uuid"]
    }
    

    TypeRoots define the path for global types. This basically indicates the .d.ts file contains declare module syntax, while packages (of type definitions) like @types/UUID do not.

    UPDATE: I used @Andrew Allen's solution, which he mentioned in the comment section below his answer that he got from this github comment (Andrew didn't update it in the answer).

    This is also resolve the issue and Its better rather writing each type definition package path.

    {
        "compilerOptions": {
            "target": "es5"
            "baseUrl": "./dependencies/nodejs/node_modules",
            "paths": {
                "*" : ["*"]
            }
        }
    }`