Reputation: 1576
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
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