Search code examples
typescriptimportmodulebundle

Typescript import from sibling folder - cannot find module


Importing modules from other folder causes typescript errors. I have a project with similar structure:

 - src
    - tsconfig.json
    - types
      - math.types.ts
    - utils
      - addNumbers.ts
 - playwright
    - tsconfig.json
    - dashboard.page.ts
  • src folder has react app in typescript and some utils that could be used in playwright
  • playwright has e2e tests for the application

Now, when I import something from src folder (for example addNumbers) in playwright/dashboard.page.ts file and then run command (in playwright folder):

tsc -p tsconfig.json

I get:


../src/utils/addNumbers.ts:4:45 - error TS2307: Cannot find module 'types/math.types' or its corresponding type declarations.

import { SomeNumberType } from 'types/math.types';

Imports work fine when I run tsc in src folder, everything is broken when I start tsc in playwright folder.

Here is tsconfig.json from playwright folder:

{
    "compilerOptions": {
      "strict": true,                                     
      "target": "es2017",                                  
      "module": "commonjs",
      "skipLibCheck": true,
      "moduleResolution": "node",                        
      "baseUrl": ".",                                      
      "esModuleInterop": true,                             
      "forceConsistentCasingInFileNames": true,           
      "paths": {                                       
        "*": ["*", "tests/*"]
      },
      "noEmit": true                       
    }
} 

Did I miss anything?


Solution

  • Based on your playwright\tsconfig.json, Typescript is throwing Cannot find module 'types/math.types' or its corresponding type declarations because it doesn't know about the src folder. When tsc runs, it uses the location of the tsconfig.json (playwright) as its scope unless it's instructed otherwise.

    You can instruct Typescript that the src directory exists and where to find it when it's used as an import.

    Here are a few ways, there are more I'm not including here since they don't seem applicable and this is already lengthy:

    1. compilerOptions.files - Explicitly list files for Typescript to include. This doesn't seem like it'd be practical for your use case given the number of files and Typescript will process them as part of the playwright tsc.
    2. compilerOptions.include - If you add src to includes, tsc will process your src files when you run in the playwright folder. Probably not what you want since you're not sharing a tsconfig between the two directories.
    3. Path Aliases - You could add a path alias for src and use that for imports in the playwright directory. One major downside is Typescript can be happy with these while compiler/runtime fail if compiler/node/etc doesn't also know about those aliases. See paths module reference for some examples that illustrate this well.
    4. Project References - The playwright\tsconfig.json provides a project reference to src. If src isn't already built when tsc is run for playwright, tsc will build src from src\tsconfig.json. The src\tsconfig.json should have composite set to true. If rootDir and declaration aren't set, it will default rootDir to the directory containing the tsconfig.json and declaration to true. declarationMap isn't required for this setup, but is useful for IDE debugging. The main change you'd make in your workflow would be using the build command tsc -b tsconfig.json instead of tsc -p tsconfig.json.

    The linked documentation points out implementation details and caveats for each option that could impact specific projects.

    Option #3 Example

    playwright\tsconfig.json

    {
       "compilerOptions":{
          "strict":true,
          "target":"es2017",
          "module":"commonjs",
          "skipLibCheck":true,
          "moduleResolution":"node",
          "baseUrl":".",
          "esModuleInterop":true,
          "forceConsistentCasingInFileNames":true,
          "paths":{
             "@src/*":[
                "../src/*"
             ],
             "*":[
                "*",
                "tests/*"
             ]
          },
          "noEmit":true
       }
    }
    

    Import in playwright directory with @src/types/...

    Option #4 Example

    playwright\tsconfig.json

    {
       "compilerOptions":{
          "strict":true,
          "target":"es2017",
          "module":"commonjs",
          "skipLibCheck":true,
          "moduleResolution":"node",
          "baseUrl":".",
          "esModuleInterop":true,
          "forceConsistentCasingInFileNames":true,
          "paths":{
             "*":[
                "*",
                "tests/*"
             ]
          },
          "noEmit":true
       },
       "references":[
          {
             "path":"../src"
          }
       ]
    }
    

    src\tsconfig.json

    {
       "compilerOptions":{
          "_comment": "Include your other compiler options",
          "composite":true,
          "declaration":true,
          "rootDir":".",
          "declarationMap":true
       }
    }