AngularDebutant
AngularDebutant

Reputation: 1576

Import from index.ts returns undefined

I have this very simple file structure consisting of

In index.ts I have the following

export const a = 3

and in my-service.ts I do,

import { a } from '../index'

console.log(a);

this logs undefined.

When I do

setTimeout(() => console.log(a), 100);

it logs 3.

I am running the solution using

ts-node -r tsconfig-paths/register ./src/index.ts

my tsconfig looks like

{
  "extends": "../../tsconfig.json",
  // "include": ["src/**/*.ts"],
  "compilerOptions": {
    "outDir": "dist",
    "allowJs": false,
    "baseUrl": ".",
    "rootDir": "src",
    "composite": true
  },
  "references": [
    {
      "path": "../shared-tools"
    }
  ],
  "exclude": ["dist"],
  "include": ["src/**/*.ts"]
}

Which extends the following root tsconfig

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    // "baseUrl": ".",
    "allowJs": false,
    "sourceMap": true,
    "declaration": true,
    "declarationMap": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "types": [],
    "typeRoots": [
      "./node_modules/@types"
    ],
    "paths": {
      "@project/shared-tools": [
        "../shared-tools/src"
      ]
    }
  }
}

Upvotes: 2

Views: 3152

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074989

That symptom suggests you have a cycle of dependencies between index.ts and my-services.ts — in addition to the dependency you've described on a in index.ts, index.ts relies on something supplied (directly or indirectly) by my-services.ts. This is called a cyclic dependency. A depends on B which depends on A.

With JavaScript's now-standard ESM static module syntax, cycles are possible but normally they can be resolved without your having to worry about them. In cases where they can't, instead of getting undefined, you get an error (if you use const as you have).

But you're telling TypeScript to rewrite your module syntax into Node's original CommonJS-inspired syntax (module.exports and require() calls). With CJS, cycles can result in your seeing undefined for things that later have a value assigned when all the module code has been run.

Ideally, the solution is to remove the cyclic dependency. If index.ts has to depend on my-services.ts, perhaps move whatever my-services.ts imports from index.ts to something else they both import from. That way, A and B both depend on C rather than on each other.

If for some reason you can't do that, you might try telling TypeScript to output JavaScript module syntax instead ("module": "ES2015" or "module": "ESNext"). Since that does static resolution (if you don't use dynamic import()), it may be able to handle the cycles for you. Node now has robust support for ESM.

Or you may not need to handle it. Even in CJS, cyclic dependencies are really only a problem if you use the dependencies in the initial module code rather than in code that runs later in response to an event (as you saw by wrapping your code using a in a setTimeout). So if the resources involved in the cycles are only needed to (for instance) respond to a network request, you may not need to worry about it at all. There's a bit more about cycles in CJS modules in Node here.

Upvotes: 4

Related Questions