paths maps import specifiers to files: "@lib/*": ["./src/lib/*"] lets import … from "@lib/format.js" find src/lib/format.js. Paths are resolved relative to the config file that sets them. baseUrl is an older companion that resolves bare names from one folder; it has not been needed for paths since TypeScript 4.1 and is deprecated from 6.0.
The catch for a JavaScript project: paths only tells the checker where to look. Nothing rewrites your imports, so Node.js, which reads your files as they are, does not know @lib and fails at run time. For code Node.js runs, the imports field in package.json gives you the same alias in a form both understand, as the third example shows.
At a glance
| Write | In a project Node.js runs, prefer a package.json imports field ("#lib/*"), which Node.js also understands. Use paths for names only a bundler or the checker resolves |
|---|---|
| On the command line | No flag: only a config file can set it |
| Type | object |
| Default in TypeScript 5.9 | not set |
| TypeScript's description | Specify a set of entries that re-map imports to additional lookup locations. |
| In the quick-start setups |
|
The description is quoted from TypeScript's own option list (Apache-2.0); see the notices. The setup values are the checked configs on the quick-start pages.
Examples
import { formatPrice } from "@lib/format.js";error TS2307: Cannot find module '@lib/format.js' or its corresponding type declarations.
console.log(formatPrice(1999));jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"src/**/*.js"
]
} import { formatPrice } from "@lib/format.js";
console.log(formatPrice("19.99"));error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": [],
"paths": {
"@lib/*": [
"./src/lib/*"
]
}
},
"include": [
"src/**/*.js"
]
} import { formatPrice } from "#lib/format.js";
console.log(formatPrice("19.99"));error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"src/**/*.js"
]
} The imports field instead
Node.js supports subpath imports: names starting with #, mapped in package.json and private to the package. TypeScript reads them under nodenext and bundler with no config, so the alias works in the checker and in Node.js alike.
{
"private": true,
"type": "module",
"imports": {
"#lib/*": "./src/lib/*"
}
} When paths is the right tool
When something else resolves the alias at run time: a bundler configured with the same alias, a test runner, or a monorepo where workspace links provide the package names and paths points the checker at the sources (see extends). Keep the two lists in sync, since nothing checks that they agree.
Gotchas
pathscannot be set on the command line, andtsc --initcannot write it; it lives only in a config file.- A
pathsentry in a base config that othersextendis resolved relative to the base file, not to the config that extends it.
Sources
- TSConfig Reference: paths · TypeScript
- TSConfig Reference: baseUrl · TypeScript
- TypeScript 2.0 release notes · TypeScript handbook
- TypeScript 4.1 release notes · TypeScript handbook
- TypeScript 6.0 release notes · TypeScript handbook
- Modules: Packages, subpath imports · Node.js docs
Last reviewed 20 October 2026.