
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
node-typescript-resolver
Advanced tools
Companion loader for Node.js's built-in TypeScript support that adds TypeScript-aware import resolution
A companion loader for Node.js's built-in TypeScript support that adds TypeScript-aware import resolution.
Note: This package does not transform TypeScript code. It works alongside Node.js 22's built-in TypeScript support (type stripping) or
--experimental-transform-typesflag to provide proper module resolution for TypeScript imports.
This package provides a fast and efficient TypeScript module resolver for Node.js that supports:
npm install node-typescript-resolver
Node.js's built-in TypeScript support does not perform import elision—it doesn't analyze which imports are used only as types. This applies to both type stripping mode and --experimental-transform-types mode. Imports like import { SomeType } from './module' will remain in the emitted JavaScript—causing runtime errors in ESM modules if SomeType is only a type (CommonJS will silently get undefined).
We believe --experimental-transform-types should handle import elision, and this may improve in future Node.js versions (the flag is still experimental). Check Node.js TypeScript documentation for updates. Until then, the solution is to use explicit type-only import syntax (import type { ... } or import { type ... }), enforced through ESLint and TypeScript configuration.
npm install -D typescript-eslint
Run this command to automatically fix all type-only imports in your codebase:
npx eslint --no-config-lookup --ext .ts,.tsx,.mts,.cts \
--parser @typescript-eslint/parser \
--plugin @typescript-eslint/eslint-plugin \
--rule '@typescript-eslint/consistent-type-imports: [error, {prefer: type-imports, fixStyle: separate-type-imports}]' \
--fix .
Add this rule to your ESLint configuration to catch violations automatically:
// eslint.config.mjs
{
rules: {
"@typescript-eslint/consistent-type-imports": [
"error",
{
prefer: "type-imports",
fixStyle: "separate-type-imports",
},
],
},
}
See @typescript-eslint/consistent-type-imports documentation for more options.
Add this compiler option to make TypeScript enforce explicit type-only syntax:
{
"compilerOptions": {
"verbatimModuleSyntax": true
}
}
This ensures any import without a type modifier is preserved in the output, making the behavior predictable and compatible with Node.js type stripping.
See verbatimModuleSyntax documentation for details.
Use the loader alongside Node.js's built-in TypeScript support to enable TypeScript-aware module resolution:
# Node.js 22.15.0+ with built-in TypeScript support (type stripping)
node --import node-typescript-resolver your-app.ts
# Or with --experimental-transform-types for type transformations
node --experimental-transform-types --import node-typescript-resolver your-app.ts
# For CommonJS TypeScript entry points, use --require instead
node --require node-typescript-resolver entry.cts
This allows you to write TypeScript imports like:
// Import without extension - resolves to helper.ts or helper.js
import { helper } from "./helper";
// Import directory - resolves to ./components/index.ts
import { Button } from "./components";
// Import with TypeScript path alias (from tsconfig.json)
import { utils } from "@lib/utils";
// Type-only imports from type-only packages
import { type Writable } from "type-fest";
// Standard imports still work
import { something } from "./module.ts";
The resolver automatically detects and uses tsconfig.json for path alias resolution:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@lib/*": ["src/lib/*"],
"@utils": ["src/utils/index.ts"],
"@components/*": ["src/components/*"]
}
}
}
With this configuration, you can use imports like:
import { Button } from "@components/Button";
import { helper } from "@lib/helpers";
import { format } from "@utils";
The loader provides both asynchronous and synchronous resolve hooks, enabling full support for import.meta.resolve() with TypeScript files:
// Resolve extensionless TypeScript imports
const helperPath = import.meta.resolve("./helper"); // Resolves to ./helper.ts
// Resolve with explicit .ts extension
const modulePath = import.meta.resolve("./module.ts");
// Resolve path aliases from tsconfig.json
const utilsPath = import.meta.resolve("@lib/utils");
// Use the resolved path
const module = await import(helperPath);
This is powered by the synchronous resolve hook (resolveSync), which Node.js uses internally when calling import.meta.resolve(). The sync hook was enabled by module.registerHooks() API added in Node.js 22.15.0. Both the async and sync hooks provide the same resolution capabilities:
The synchronous resolve hook also enables CommonJS require() to work seamlessly with TypeScript files:
// main.cjs
// Require extensionless TypeScript module
const helper = require("./helper"); // Resolves to ./helper.ts
// Require with explicit .ts extension
const module = require("./module.ts");
// Require path aliases from tsconfig.json
const utils = require("@lib/utils");
This means you can:
Note: While Node.js's built-in TypeScript support works with CommonJS files (.cjs), the TypeScript files themselves should use ESM syntax (export/import). The loader enables CommonJS code to require() those TypeScript ESM modules.
Worker threads work with TypeScript files. The Worker constructor requires the .ts extension for the worker file itself, but imports inside the worker support extensionless resolution:
// Worker file must have .ts extension
const worker = new Worker(new URL("./worker.ts", import.meta.url), {
execArgv: process.execArgv, // Pass loader to worker
});
// Inside worker.ts - extensionless imports work
import { helper } from "./helper"; // Resolves to ./helper.ts
The loader supports type-only imports from packages that only export TypeScript types (like type-fest):
import { type Writable } from "type-fest";
import { type JsonValue } from "type-fest";
Node.js Limitation Workaround: Node.js currently doesn't support loading .d.ts files from node_modules when using built-in TypeScript support. The loader works around this by:
.d.ts file in node_modules"types" export condition to find the correct type definitionsThis allows you to use TypeScript's import { type } syntax with type-only packages without any runtime errors.
This loader is designed to be non-intrusive and provides both async and sync resolve hooks:
Always tries default Node.js resolution first
ERR_MODULE_NOT_FOUND, ERR_PACKAGE_PATH_NOT_EXPORTED, or other resolution errorsimport.meta.resolve()Fallback resolution - When default resolution fails, the loader tries:
Dual resolution modes
resolve) - Used for dynamic imports and regular import statementsresolveSync) - Used by import.meta.resolve() and CommonJS require() for synchronous resolutionEfficient caching
clearCache()This approach ensures:
This package is designed for high performance:
This package is built for reliability and production use:
module.registerHooks() (added in Node.js 22.15.0)type-festimport.meta.resolve() and createRequire() supportmodule.registerHooks() for sync resolution)--experimental-transform-types flag (this package only handles import resolution, not code transformation)# Install dependencies
npm install
# Build the package
npm run build
# Run tests
npm test
# Run tests with TypeScript directly (Node.js 22.15.0+)
npm run test:ts
Contributions are welcome! Please feel free to submit a Pull Request.
MIT
Konstantin Vyatkin tino@vtkn.io
FAQs
Companion loader for Node.js's built-in TypeScript support that adds TypeScript-aware import resolution
We found that node-typescript-resolver demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.