tsconfig
`tsconfig` is a small Node.js library for locating, reading, and parsing a `tsconfig.json` file. Give it a working directory and it can search upward like the TypeScript compiler, or point it at a specific file or directory. It strips a UTF-8 byte-order mark and JSON comments before parsing, and offers asynchronous and synchronous versions of most functions. Despite the package name, it does not provide a reusable compiler configuration and it does not run `tsc`; it only returns the raw JSON object it found.
Keep it only for simple file discovery in existing Node tools. New code should usually choose `get-tsconfig` or the TypeScript compiler API because raw JSON parsing is not the same as understanding a modern `tsconfig.json`.
Use it if
- You maintain an older Node tool that already depends on this package and only needs to find the nearest `tsconfig.json`
- You need both asynchronous and synchronous helpers for locating and reading one TypeScript configuration file
- You need to accept comments, a byte-order mark, or a completely empty file before handing the raw object to your own code
- You deliberately want a tiny API that returns the file path alongside the unprocessed configuration object
- You need compiler-accurate semantics: `load` only parses one file and returns `config: any`; the source never follows `extends`, merges inherited options, expands `include`, or converts paths the way TypeScript does
- You need modern JSONC parsing: the implementation removes comments and then calls `JSON.parse`, so trailing commas accepted by normal `tsconfig.json` files still throw a syntax error
- You are starting a new project and care about active maintenance: version 7.0.0 was published in August 2017, the last implementation change was in January 2019, and the only later commit added `SECURITY.md` in December 2023
- You need useful static types for compiler options: the shipped declaration exposes both `config` and parse results as `any`, so misspelled option names pass type checking
- You need browser or edge-runtime code: the implementation directly imports Node's `fs` and `path` modules and performs filesystem traversal, with no browser entry point
Setup reality
Installation is only `npm install tsconfig`; there are no peer dependencies, credentials, native extensions, environment variables, or config files belonging to the package itself. It ships CommonJS output and a declaration file, so `require('tsconfig')` is the safest runtime form and TypeScript consumers can use named imports when their module settings interoperate with CommonJS. The important setup work is in your expectations. Passing only a working directory makes `find` and `load` walk toward the filesystem root and select the nearest file named exactly `tsconfig.json`. Passing a second argument changes the behavior: a file is read directly, while a directory must contain `tsconfig.json`, and a missing explicit target rejects or throws instead of returning an empty result. When an implicit search finds nothing, `load` does not fail; it returns `{ config: { files: [], compilerOptions: {} } }` with no `path`, which can make a missing project configuration look valid unless you check the path. Parsing is intentionally shallow. Comments and a leading byte-order mark are removed, and an empty file becomes `{}`, but trailing commas fail because the final parser is `JSON.parse`. The library does not resolve `extends`, validate compiler options, apply TypeScript defaults, or normalize relative paths. Its declarations also return `any` for configuration data, so production callers should validate the object themselves. Finally, the four runtime dependencies include two separate `@types` packages because this release predates modern bundled declarations in those utilities. None require a build step, but the dependency shape and 2017 release date matter if you enforce strict age or supply-chain policies.
Patterns
Load the nearest tsconfig.json asynchronouslyload-nearest-config
const { load } = require('tsconfig');
const result = await load(process.cwd());
if (!result.path) throw new Error('No tsconfig.json found');
console.log(result.path, result.config.compilerOptions);Without a filename, the search walks upward to the filesystem root. Check `path`, because a missing file returns a default configuration instead of rejecting.
Load the nearest config synchronouslyload-nearest-sync
const { loadSync } = require('tsconfig');
const { path, config } = loadSync(__dirname);
if (path) {
console.log(config.compilerOptions || {});
}The synchronous form blocks on filesystem work. It is reasonable during process startup or in a short CLI, but avoid it on a request-handling path.
Find a config without parsing itfind-config-path
const { find } = require('tsconfig');
const configPath = await find('/workspace/packages/widget/src');
if (configPath) {
console.log(`Using ${configPath}`);
}`find` only looks for the exact name `tsconfig.json` while walking upward. It resolves to `undefined` when the search reaches the root.
Find a config path synchronouslyfind-config-path-sync
const { findSync } = require('tsconfig');
const configPath = findSync(process.cwd());
const exitCode = configPath ? 0 : 1;
console.log(configPath || 'No tsconfig.json found');
process.exitCode = exitCode;A not-found result is `undefined`, not an exception. The search may select a parent workspace config when the current package has none.
Resolve an explicitly named config fileresolve-explicit-file
const { resolve } = require('tsconfig');
const configPath = await resolve(process.cwd(), 'configs/tsconfig.build.json');
console.log(configPath);The second argument is resolved relative to the first. Unlike implicit search, an explicit nonexistent path rejects with `TypeError`.
Resolve tsconfig.json inside a directoryresolve-config-directory
const { resolveSync } = require('tsconfig');
const configPath = resolveSync(process.cwd(), 'packages/server');
console.log(configPath);When the second argument names a directory, the library appends `tsconfig.json`; it does not search upward from that directory.
Load a build-specific configload-alternate-config
const { load } = require('tsconfig');
const { path, config } = await load(
process.cwd(),
'tsconfig.build.json'
);
console.log(path, config.compilerOptions);The returned object is the raw contents of that one file. An `extends` property is not followed or merged.
Read a config at a known pathread-known-config
const { readFile } = require('tsconfig');
const config = await readFile('/workspace/app/tsconfig.json');
console.log(config.include || []);`readFile` strips comments and a leading byte-order mark, but filesystem errors and JSON syntax errors reject the promise.
Read a known config synchronouslyread-known-config-sync
const { readFileSync } = require('tsconfig');
try {
const config = readFileSync('./tsconfig.json');
console.log(config);
} catch (error) {
console.error('Invalid or unreadable config:', error.message);
}This function throws for read and parse failures. It does not distinguish them with a package-specific error class.
Parse an in-memory config with commentsparse-json-comments
const { parse } = require('tsconfig');
const config = parse(`{
// accepted by this parser
"compilerOptions": { "strict": true }
}`, 'tsconfig.json');Comments are removed before `JSON.parse`, but trailing commas are still invalid. The filename argument is required by the declaration but is not used in error messages.
Accept an empty configuration filehandle-empty-config
const { parse } = require('tsconfig');
const empty = parse(' \n', 'tsconfig.json');
console.log(empty); // {}Whitespace-only input becomes an empty object. That differs from calling `JSON.parse` directly, which would throw.
Validate the untyped result before useguard-untyped-config
const { load } = require('tsconfig');
const { config } = await load(process.cwd());
const options = config && typeof config.compilerOptions === 'object'
? config.compilerOptions
: {};
if (options.strict !== undefined && typeof options.strict !== 'boolean') {
throw new TypeError('compilerOptions.strict must be boolean');
}The declaration types `config` as `any`, and the library does not validate option names or values. Add your own guard or use a typed parser.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| get-tsconfig | npm | Use it for a maintained, typed reader that resolves `extends` and can create a paths matcher |
| tsconfck | npm | Use it in build tools that need cached parsing, inherited configs, references, and native ESM |
| typescript | npm | Use the compiler API when results must exactly match the installed TypeScript version |
| cosmiconfig | npm | Use it when you are designing a general-purpose tool configuration search rather than interpreting TypeScript settings |