Skip to content

Adding typescript support#877

Open
mdkitzman wants to merge 7 commits intonode-config:masterfrom
mdkitzman:master
Open

Adding typescript support#877
mdkitzman wants to merge 7 commits intonode-config:masterfrom
mdkitzman:master

Conversation

@mdkitzman
Copy link

@mdkitzman mdkitzman commented Feb 10, 2026

This commit introduces TypeScript support to the project by adding type definitions and necessary configuration files. The main changes include:

  • Addition of tsconfig.json to configure the TypeScript compiler.
  • Creation of type definition files for existing JavaScript modules to enable type checking and IntelliSense.
  • Updates to package.json to include type information and scripts for building types.
  • Test cases to validate the TypeScript integration and ensure type safety.

This helps address #870

Summary by CodeRabbit

  • New Features

    • Published comprehensive TypeScript declarations and added runtime improvements to parsing and deferred/raw config utilities.
  • Documentation

    • README updated with TypeScript usage and typings guidance.
  • Chores

    • Added types build/check scripts, prepublish step, package.json types/typesVersions, and ignored types directory.
    • Updated devDependencies (TypeScript, ts-node, @types/node).
  • Tests

    • Added TypeScript declaration and type-check tests.

This commit introduces TypeScript support to the project by adding type definitions and necessary configuration files. The main changes include:
- Addition of `tsconfig.json` to configure the TypeScript compiler.
- Creation of type definition files for existing JavaScript modules to enable type checking and IntelliSense.
- Updates to `package.json` to include type information and scripts for building types.
- Test cases to validate the TypeScript integration and ensure type safety.
@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

Adds JSDoc-based TypeScript typings, a TypeScript build (tsconfig, npm scripts), generated declaration outputs, type-tests, and typing-related documentation and metadata across the codebase; no substantive runtime logic changes except explicit defer async resolution behavior and exported defer API.

Changes

Cohort / File(s) Summary
Package & Build
package.json, .gitignore, tsconfig.json
Add types entry, typesVersions mappings, type build/check scripts, upgrade devDependencies, and ignore types/ in VCS; add tsconfig for declaration emission.
Top-level Docs
README.md
Add TypeScript usage/documentation block for published declarations and subpath typings; minor formatting fix.
Core Config
lib/config.js
Introduce typedefs (Util, Load, LoadOptions, Parser), rename constructor to ConfigClass in docs/typedefs and instantiate exported config from new ConfigClass(); update many JSDoc method signatures and return types.
Utilities
lib/util.js
Expand JSDoc typedefs (Parser, LoadOptions), tighten signatures (generics, optionals, return types), adjust exported shape to { Util, Load } — all type-only changes.
Async handling
async.js
Add typedefs and JSDoc overloads for asyncConfig (Promise vs deferred function) and refine resolveAsyncConfigs types; documentation-only type annotations.
Defer implementation
lib/defer.js, defer.js
Document and export DeferredConfig and deferConfig typings; implement DeferredConfig.prototype.prepare/resolve with async and sync resolution paths and export them.
Parser enhancements
parser.js
Introduce generic ParserFn, add Parser.parse entrypoint, many parser implementations (xml, js, ts, coffee, yaml, json5, hjson, toml, cson, properties), lazy-loading, ES module handling, and parser registry APIs (getParser, setParser, getFilesOrder, setFilesOrder).
Raw config
raw.js
Add @constructor JSDoc to RawConfig and document raw(rawObj) return typing (RawConfig with resolve).
Type tests & harness
test/30-types-test.js, test/type-files/*.ts, test/type-files/tsconfig.json
Add tsc-based test runner and multiple TypeScript fixture files validating typings for async, config, defer, parser, raw, and util APIs; include @ts-expect-error checks and a test tsconfig for fixtures.
Type outputs
types/... (generated paths referenced)
Introduce intent to emit declaration files into types/ (via tsconfig + package.json types), with per-subpath resolution in typesVersions.

Sequence Diagram(s)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

feature-request

Suggested reviewers

  • markstos
  • lorenwest

Poem

🐇 I hopped through JSDoc, nibbling types with care,

Declarations sprouted everywhere,
TS checked the meadow, no runtime alarm,
Deferred or async, each type has its charm,
Hop, build, publish — the bunny did share.

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Adding typescript support' directly corresponds to the primary objective of this PR, which is to introduce TypeScript support through type definitions, configuration, and integration.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
package.json (1)

100-103: Inconsistent script naming convention: build:types (kebab/colon) vs checkTypes (camelCase).

Minor nit — consider aligning naming for discoverability, e.g., check:types to match build:types.

Proposed fix
-    "checkTypes": "npx -y `@arethetypeswrong/cli` --pack .",
+    "check:types": "npx -y `@arethetypeswrong/cli` --pack .",
lib/defer.js (1)

19-19: Prototype stub prepare is a no-op — acceptable for type-only purposes, but could confuse callers who construct DeferredConfig directly.

The stub DeferredConfig.prototype.prepare is always overwritten inside deferConfig(). Since DeferredConfig is now exported, a consumer could new DeferredConfig() and call prepare(), getting silent no-op behavior. Consider adding a guard or a clarifying comment.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
parser.js (2)

154-166: ⚠️ Potential issue | 🟠 Major

icedParser never returns the parsed config — it only registers the compiler.

Compare with coffeeParser (line 151) which ends with return require(filename). This function registers iced-coffee-script but implicitly returns undefined. The new JSDoc @returns {object | undefined} masks the bug by suggesting undefined is a valid outcome.

Proposed fix
 Parser.icedParser = function(filename, content) {
   Iced = require(ICED_DEP);
 
   // coffee-script >= 1.7.0 requires explicit registration for require() to work
   if (Iced.register) {
     Iced.register();
   }
+  return require(filename);
 };

297-308: ⚠️ Potential issue | 🟡 Minor

JSDoc @return {number} is inaccurate — this function can return undefined.

Line 307 explicitly returns undefined when the value is NaN. The return type should reflect this.

Proposed fix
- * `@return` {number} - Number value fo the passed variable value
+ * `@return` {number | undefined} - Number value of the passed variable value
lib/util.js (1)

1137-1141: ⚠️ Potential issue | 🟡 Minor

fromEnvironment parameter environments should be marked optional.

The implementation checks if (environments !== undefined) (line 1144), confirming it's optional, but the JSDoc types it as required {string}.

Proposed fix
-   * `@param` {string} environments the NODE_CONFIG_ENVs you want to load
+   * `@param` {string} [environments] the NODE_CONFIG_ENVs you want to load
🤖 Fix all issues with AI agents
In `@defer.js`:
- Around line 8-14: The JSDoc for DeferredConfig.prototype.prepare incorrectly
states it returns void; update the comment to return DeferredConfig so callers
(like resolveDeferredConfigs in lib/util.js) can push and later call .resolve()
on the returned instance. Specifically, change the `@returns` annotation for
DeferredConfig.prototype.prepare to reference DeferredConfig (and ensure the
implementation in deferConfig that returns obj still returns the DeferredConfig
instance).

In `@lib/config.js`:
- Around line 17-28: Fix the malformed JSDoc block and the duplicate/conflicting
typedef: add the missing leading space/star to re-align the JSDoc line (so the
entire comment attaches properly), and remove the local duplicate typedef for
LoadOptions in lib/config.js; instead reference the canonical definition from
lib/util.js (or make optionality match by adjusting the property annotations) so
LoadOptions is defined only once—locate the LoadOptions typedef in lib/config.js
and replace it with an import/alias to the LoadOptions typedef in lib/util.js
(or update the property optional markers to match util.js).

In `@lib/util.js`:
- Around line 881-887: Remove the phantom JSDoc param for `{Load} load` from the
JSDoc above the scan function so the documented signature matches the actual
function `scan(additional)`: update the JSDoc to only declare the `additional`
parameter (e.g., `@param {{name: string, config: any}[]=} additional`) and any
relevant description, ensuring the `scan` function and its `additional`
parameter are the only symbols referenced in the comment to prevent TypeScript
from misassigning the `Load` type.
- Around line 339-342: The JSDoc for cloneDeep is malformed: add explicit
parameter names and proper default-value bracket syntax so TypeScript can map
them—change the second and third `@param` entries to use names and defaults like
`@param` {number} [depth=20] Maximum depth and `@param` {boolean} [circular=true]
Handle circular references, and add a fourth `@param` documenting prototype (e.g.
`@param` {object=} prototype Optional prototype for the new object) plus a brief
`@return` description; update the description to reference the cloneDeep function
signature accordingly.

In `@package.json`:
- Around line 93-96: Add a prepublishOnly script that runs the type build so
published packages include declaration files: update package.json scripts to add
a "prepublishOnly" entry that invokes the existing "build:types" (and any other
build step if needed) so the declared "types" and "typesVersions" artifacts are
produced before npm publish; target the "build:types" script name and the
"prepublishOnly" script key in your edit.
- Line 73: Update the TypeScript dependency entry in package.json: replace the
invalid version string "typescript": "5.9.0" with a published version (e.g.,
"5.9.3") so the dependency resolves correctly; edit the existing "typescript"
dependency line in package.json to the valid published version.
🧹 Nitpick comments (4)
parser.js (2)

352-361: Union return type string[] | number makes the API harder to consume in TypeScript.

getFilesOrder() returns the full order array when called without arguments, or an index number when called with a name. This overloaded behavior produces a union return type that forces callers to narrow. Consider whether this could use JSDoc @overload annotations, or document the two calling conventions more clearly for type consumers.


37-40: Consider using explicit type arguments for the ParserFn generic in return and parameter types for consistency.

The @template [T=any] syntax in the typedef is valid and will emit correctly via tsc --declaration. However, line 335 returns ParserFn | undefined and line 343 accepts ParserFn without type arguments, while the typedef itself defines a generic type. For clarity, explicitly specify the type argument (e.g., ParserFn<any> | undefined or ParserFn<object>) in these usages to match the generic definition.

test/30-types-test.js (1)

19-36: Consider adding a timeout to spawnSync to prevent the test from hanging.

If tsc encounters an issue that causes it to hang, the test runner will block indefinitely.

Proposed fix
   const result = spawnSync(process.execPath, [tscPath, '-p', tsconfigPath], {
     cwd: repoRoot,
-    encoding: 'utf8'
+    encoding: 'utf8',
+    timeout: 60_000
   });
lib/config.js (1)

416-418: loadFileConfigs return type Config may be misleading.

newLoad.config (line 432) is a plain object without ConfigClass prototype methods. Typing it as Config (alias for ConfigClass) implies it has .get(), .has(), etc. Consider typing the return as object or adding a note that the caller is responsible for calling attachProtoDeep.

This includes updates to the package.json and package-lock.json files to
update dependencies, as well as some changes to the defer.js,
lib/config.js, and lib/util.js files based on the feedback received
about the type information.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@lib/util.js`:
- Line 29: Fix the JSDoc typo for the nodeEnv property: change the description
string "commo-separated" to "comma-separated" in the JSDoc comment that
documents the nodeEnv property (the line mentioning "@property {string[]=}
nodeEnv") so the comment correctly reads "NODE_ENV value or comma-separated
list".
- Around line 979-982: The JSDoc block above the Load class method that returns
ConfigSource[] is misindented; fix the indentation so the opening /**, the
`@returns` {ConfigSource[]}, and the closing */ are aligned with the other JSDoc
blocks in the Load class. Locate the JSDoc immediately preceding the Load class
method (the comment that documents the method returning ConfigSource[]) and
adjust leading spaces to match the class's JSDoc style so the entire block is
visually aligned with the method body.
🧹 Nitpick comments (3)
lib/config.js (2)

92-95: Unbounded @template T on get() — TypeScript cannot infer it from parameters.

Since property is typed as string, there's no way for TS to infer T from JSDoc alone. Callers will get unknown unless they use a cast. This is acceptable for a config library (it's a common pattern in .d.ts files where users write config.get<string>('key')), but be aware that JSDoc-based consumers won't benefit from the generic — it will effectively be any/unknown.


404-406: loadFileConfigs return type Config may be misleading.

The method returns newLoad.config, which is a plain object ({}), not a ConfigClass instance. Typing it as Config (alias for ConfigClass) could confuse consumers expecting prototype methods like get() and has() on the returned object. Consider typing the return as object to be more accurate, or documenting that the return is a raw config data object.

lib/util.js (1)

7-7: Parser typedef is duplicated in both lib/config.js (line 10) and lib/util.js (line 7).

Both files define @typedef {typeof import('./../parser')} Parser. Consider defining it in one place and importing from there to keep a single source of truth, though this is minor given it's a type-only declaration.

* @property {string} nodeEnv - NODE_ENV value or commo-separated list
* @property {string} hostName - hostName for host-specific loads
* @property {string=} configDir - config directory location, absolute or relative to cwd()
* @property {string[]=} nodeEnv - NODE_ENV value or commo-separated list
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo: "commo-separated" → "comma-separated".

- * `@property` {string[]=} nodeEnv - NODE_ENV value or commo-separated list
+ * `@property` {string[]=} nodeEnv - NODE_ENV value or comma-separated list
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @property {string[]=} nodeEnv - NODE_ENV value or commo-separated list
* `@property` {string[]=} nodeEnv - NODE_ENV value or comma-separated list
🤖 Prompt for AI Agents
In `@lib/util.js` at line 29, Fix the JSDoc typo for the nodeEnv property: change
the description string "commo-separated" to "comma-separated" in the JSDoc
comment that documents the nodeEnv property (the line mentioning "@property
{string[]=} nodeEnv") so the comment correctly reads "NODE_ENV value or
comma-separated list".

Comment on lines -14 to -57
/**
* <p>Application Configurations</p>
*
* <p>
* The config module exports a singleton object representing all
* configurations for this application deployment.
* </p>
*
* <p>
* Application configurations are stored in files within the config directory
* of your application. The default configuration file is loaded, followed
* by files specific to the deployment type (development, testing, staging,
* production, etc.).
* </p>
*
* <p>
* For example, with the following config/default.yaml file:
* </p>
*
* <pre>
* ...
* customer:
* &nbsp;&nbsp;initialCredit: 500
* &nbsp;&nbsp;db:
* &nbsp;&nbsp;&nbsp;&nbsp;name: customer
* &nbsp;&nbsp;&nbsp;&nbsp;port: 5984
* ...
* </pre>
*
* <p>
* The following code loads the customer section into the CONFIG variable:
* <p>
*
* <pre>
* const CONFIG = require('config').customer;
* ...
* newCustomer.creditLimit = CONFIG.initialCredit;
* database.open(CONFIG.db.name, CONFIG.db.port);
* ...
* </pre>
*
* @module config
* @class Config
*/
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seemed duplicative of the JSDocs just below, so I removed them. If there is something in here that is important to maintain with the docs below, I'm happy to update it.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@lib/config.js`:
- Around line 404-406: The return type for loadFileConfigs is misleading because
it returns newLoad.config (a plain object from Load) rather than a ConfigClass
instance; either call attachProtoDeep on newLoad.config before returning (so it
becomes a ConfigClass-like object with get/has) or change the declared return
type to a plain object/precise shape (e.g., object or Record<string, any>)
instead of Config/ConfigClass; update the loadFileConfigs implementation or its
type annotation accordingly and ensure references to newLoad.config and
attachProtoDeep are the targets for the change.
🧹 Nitpick comments (1)
lib/config.js (1)

92-94: @template T on get() won't provide type narrowing without a cast by the caller.

The generic T is unconstrained and not linked to any input parameter, so TypeScript will infer T as unknown unless the caller explicitly provides a type argument (e.g., config.get<string>('key')). This is a known limitation of JSDoc-based generics and is acceptable, but worth documenting for consumers or considering a typed overload in the .d.ts file.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
parser.js (1)

297-308: ⚠️ Potential issue | 🟡 Minor

numberParser return type should include undefined.

Line 307 returns undefined when the value is NaN, but the JSDoc on line 303 declares @return {number}. The generated .d.ts will incorrectly tell consumers this always returns a number.

Proposed fix
- * `@return` {number} - Number value fo the passed variable value
+ * `@return` {number | undefined} - Number value of the passed variable value
🧹 Nitpick comments (3)
package.json (1)

62-77: TypeScript is pinned to an exact version — consider using a caret range.

All other devDependencies use range specifiers (^ or ~), but typescript is pinned to 5.9.3. This means contributors won't automatically get patch fixes. Consider ^5.9.3 for consistency, unless the exact pin is intentional.

Proposed change
-    "typescript": "5.9.3",
+    "typescript": "^5.9.3",
lib/config.js (1)

92-95: Unconstrained @template T on get will infer as unknown in generated declarations.

Since there's no way to bind T to the config shape from JSDoc alone, the generated .d.ts will type the return as T which callers must narrow themselves. This is acceptable but worth noting — consumers will likely need type assertions like config.get<string>('key') or typed wrappers.

parser.js (1)

352-361: getFilesOrder return type union makes consuming code awkward.

string[] | number accurately reflects the overloaded behavior, but this is a pre-existing API design choice. For the generated .d.ts, consumers will need type narrowing on every call. This is fine to leave as-is for now.

The test files were added in a `types` directory in the `test` directory
but the `.gitignore` file was setup to ignore those, and I forgot to add
them in.
@jdmarshall
Copy link
Collaborator

Unfortunately this collides with the PR I just landed. But If you want to clean it up I'll try to land it before I merge the other 2 PRs which will again break it.

@jdmarshall
Copy link
Collaborator

LGTM @markstos @lorenwest ?

@mdkitzman
Copy link
Author

@jdmarshall Just updated with the changes and added in JSDocs for the new files

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants