ts-node
ts-node copied to clipboard
Resolution prefers .json over .ts when extension omitted from import specifier
Search Terms
".default is not a constructor"
Expected Behavior
Can instantiate class instance even if there is a json file with the same name as the class
Actual Behavior
Cannot instantiate class instance if there is a json file with the same name as the class
this.config = new Config();
^
TypeError: Config_1.default is not a constructor
just because a file config.json is also present in the folder where the Config.ts class resides.
Steps to reproduce the problem
Create a typescript class and export module. Add a json file with the same name as the class. For example create a Config.ts class and add a Config.json file in the same folder.
But code runs fine is build with tsc and then run as JavaScript in node. Problem seems only to occur in ts-node.
Minimal reproduction
Source folder content:
Config.ts:
export default class Config {
constructor() {}
test: string = "Test";
}
ConfigurationManager.ts:
import Config from "./Config";
export default class ConfigurationManager {
private config: Config;
constructor() {
this.config = new Config();
}
public getConfiguration(): Config {
return this.config;
}
}
app.ts:
import ConfigurationManager from "./ConfigurationManager";
const configuration = new ConfigurationManager();
console.log(configuration.getConfiguration().test);
config.json:
{}
Specifications
- ts-node version: 10.9.1 (also tested 9.1.1)
- node version: 16.14.0
- TypeScript version: 4.7.4 (also tested 4.5.4)
- tsconfig.json, if you're using one:
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"esModuleInterop": true,
"outDir": "./build",
"rootDir": "./source",
"target": "es6",
"skipLibCheck": true,
"strict": true
}
}
- package.json:
{
"name": "ts-node-issue",
"version": "1.0.0",
"description": "minimal-example",
"scripts": {
"start": "ts-node source/app.ts",
"build": "tsc"
},
"keywords": [],
"author": "sjoerd222888",
"dependencies": {
"ts-node": "^10.9.1",
"typescript": "^4.7.4"
},
"devDependencies": {
"@babel/preset-typescript": "^7.16.7"
}
}
- Operating system and version: macOS Monterey 12.5 & Windows 10
- If Windows, are you using WSL or WSL2?: No
I suspect this is an issue with module resolution, and the part about classes is a red herring. "Module resolution" refers to the process where node sees import from "./Config";
and converts the string "./Config"
into an absolute path on the filesystem.
Here is my guess:
When there are multiple files on the filesystem with the same name and different extensions:
-
foo.js
-
foo.json
-
foo.ts
...and when you omit the file extension from an import, node will load them in this order:.js
first, then.json
, then.ts
.
If my hypothesis is correct, then you will not see the problem in ts-node with foo.js
and foo.json
, but you will see the problem with foo.ts
and foo.json
.
Are you able to simplify your reproduction to really narrow down and focus on this hypothesis to prove if it's correct or not? That will help figure out what to do next.
This requires "allowJs": true
in tsconfig.json and then this works, yes. I assume your hypothesis makes sense.