AidenDean
AidenDean

Reputation: 348

"Module not found" when extending a class

Every time when I try to extend the abtract class Command and I try to compile I get the error: Error: Cannot find module 'src/classes/commandBase'

My project has the following structure:

   ├────tsconfig.json
   └────src
        ├───classes
        │   └───commandBase.ts
        ├───commands
        │   └───ping.ts
        ├───handlers
        ├───listeners
        └───models

commandBase.ts

abstract class Command {
  name: string;
  abstract execute(client: Client, message: Message, args: string[]): void;

  constructor(name: string) {
    this.name = name;
  }
}
export default Command;

ping.ts

import Command from "src/classes/commandBase";

class Ping extends Command {
  constructor() {
    super("ping");
  }
  async execute(
    client: Client,
    message: Message,
    args: string[]
  ): Promise<void> {
    // ...
  }
}

export default Ping;

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "CommonJS",
    "rootDir": "./src/",
    "outDir": "./dist/",
    "strict": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "removeComments": true,
    "typeRoots": ["node_modules/@types"],
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {}
  },
  "include": ["./**/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

However

If I change import Command from "src/classes/commandBase"; to import Command from "../classes/commandBase"; everything is working fine, no errors appear. The thing is I use autocompletion (VS Code) and everytime I import "Command" it automatically writes "src/classes/commandBase";

Another thing is if I create a new file which doesnt extend Command but imports it everything is also working as intended. So I am very confused about this behavior.

File foo.ts

import Command from "src/classes/commandBase";

const foo: Command = {
  name: "",
  execute: function (client: Client, message: Message, args: string[]): void {
    throw new Error("Function not implemented.");
  }
}

Upvotes: 3

Views: 353

Answers (2)

Gonzalo Cugiani
Gonzalo Cugiani

Reputation: 660

That is because your Classes and Commands folders are in the src directory, you don't need to leave that directory. Your path should be something like ./classes/ or ./commands/Ping if you're running from the main folder.

Here is not specified "./" before source folder

../ indicates that you're leaving the current directory to go up one ./ is in the current directory

Add in your settings JSON "typescript.preferences.importModuleSpecifier": "relative",

This configuration run to me, check structure and configuration are you using about module in tsconfig.json, and choice 'absolute paths' instead to avoid relative path cases as "../../../"

import Command from "@classes/commandBase";

tsconfig.json

/* Modules */
    "module": "commonjs",
    "rootDir": "./src",
    "baseUrl": ".",
    "paths": { /* Set absolute paths with '@path' */
      "@config/*": ["./src/config/*"],
      "@controllers/*": ["./src/controllers/*"],
      "@services/*": ["./src/services/*"],
      "@classes/*": ["./src/classes/*"],
      "@commands/*": ["./src/commands/*"],
      "@routes/*": ["./src/routes/*"],
      "@pages/*": ["./src/pages/*"],
      "@utils/*": ["./src/utils/*"],
      "@middlewares/*": ["./src/middlewares/*"]
    }

    // Allow multiple folders to be treated when resolving modules
    "typeRoots": [
      "./node_modules/@types",
      "./src/@types"

package.json

    "plugins": [
      [
        "module-resolver",
        {
          "alias": {
            "@config": "./src/config",
            "@controllers": "./src/controllers",
            "@services": "./src/services",
            "@classes": "./src/classes",
            "@commands": "./src/commands",
            "@routes": "./src/routes",
            "@pages": "./src/pages",
            "@utils": "./src/utils",
            "@middlewares": "./src/middlewares"
          }
        }
      ]
    ]
  }
}

Upvotes: 1

tenshi
tenshi

Reputation: 26344

Set typescript.preferences.importModuleSpecifier to relative in your settings (JSON):

"typescript.preferences.importModuleSpecifier": "relative"

It doesn't error in foo.ts because Command is being used as a type and not as a class to extend from. Types are erased at runtime, so there is no error because the import has been erased.

Upvotes: 1

Related Questions