Skip to content

refactor: typescript strict types#1197

Merged
chimurai merged 6 commits intomasterfrom
refactor-strict-type
Apr 6, 2026
Merged

refactor: typescript strict types#1197
chimurai merged 6 commits intomasterfrom
refactor-strict-type

Conversation

@chimurai
Copy link
Copy Markdown
Owner

@chimurai chimurai commented Apr 6, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Safer response header handling to avoid incorrect assignments and conditional header normalization.
  • Refactor

    • Broader TypeScript typing improvements across request/response, routing, and path-rewriting internals for stronger compile-time checks.
    • More explicit method and field typing to reduce runtime ambiguity.
  • Tests

    • Updated tests to use realistic request objects and stronger type annotations.
  • Chores

    • Enabled stricter TypeScript compiler checks (noImplicitAny).

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d2befd9-5ef1-4c89-8dd5-c93a7cdced13

📥 Commits

Reviewing files that changed from the base of the PR and between 08b6a78 and 929461b.

📒 Files selected for processing (1)
  • src/handlers/response-interceptor.ts
✅ Files skipped from review due to trivial changes (1)
  • src/handlers/response-interceptor.ts

📝 Walkthrough

Walkthrough

This PR strengthens TypeScript typings across the codebase: introduces a reusable PathRewriteConfig<TReq>, makes core router/path-rewriter/proxy APIs generic over request/response types, tightens response header handling, annotates tests, and enables noImplicitAny.

Changes

Cohort / File(s) Summary
Types
src/types.ts
Added exported PathRewriteConfig<TReq>; updated Options.pathRewrite? to use it and constrained Options.router? object form to Record<string, ProxyServerOptions['target']>.
Proxy & Routing Core
src/http-proxy-middleware.ts, src/path-rewriter.ts, src/router.ts
Made core APIs generic over TReq/TRes; added concrete return type for pathRewriter; replaced namespace imports with named imports; adjusted private method signatures and path-rewrite invocation (req.url as string, req).
Response Handler
src/handlers/response-interceptor.ts
Made copyHeaders generic and explicitly typed; conditional assignment of statusCode/statusMessage; guarded set-cookie normalization and response.headers mutation; cast header values to `number
Tests — E2E & Unit
test/e2e/express-error-middleware.spec.ts, test/e2e/express-router.spec.ts, test/e2e/http-proxy-middleware.spec.ts, test/e2e/router.spec.ts, test/e2e/test-kit.ts, test/unit/path-filter.spec.ts, test/unit/path-rewriter.spec.ts, test/unit/router.spec.ts, test/types.spec.ts
Tightened test typings: typed Express handlers (RequestHandler, ErrorRequestHandler), annotated Filter/Logger, constructed real IncomingMessage instances from Socket in unit tests, added @ts-expect-error checks, and adapted calls to match new generics.
Build Config
tsconfig.json
Enabled noImplicitAny to disallow implicit any types across the project.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hop, hop, hooray! With types so neat,
I bounded through generics on agile feet.
Headers tidy, rewrites in line,
No implicit any to dim the shine.
Code hops onward — future's sweet! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: typescript strict types' directly and accurately summarizes the main change across all modified files—enabling strict TypeScript typing throughout the codebase.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor-strict-type

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

@chimurai chimurai added this to the v4 milestone Apr 6, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 6, 2026

npm i https://pkg.pr.new/http-proxy-middleware@1197

commit: 929461b

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 6, 2026

Coverage Status

coverage: 93.103% (-0.2%) from 93.333%
when pulling 929461b on refactor-strict-type
into 7aea19f on master.

Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (3)
test/unit/path-rewriter.spec.ts (1)

161-161: Redundant cast: as unknown as any is unnecessary.

The async function async () => {} already satisfies PathRewriteConfig (matches the async function variant). The double cast is redundant.

♻️ Proposed simplification
     it('should not throw when async function config is provided', () => {
-      expect(badFn((async () => {}) as unknown as any)).not.toThrow(Error);
+      expect(badFn(async () => '')).not.toThrow(Error);
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/path-rewriter.spec.ts` at line 161, The test assertion uses a
redundant double-cast on the async lambda; update the call to badFn to pass the
async function directly (replace badFn((async () => {}) as unknown as any) with
badFn(async () => {})) so the async function variant of PathRewriteConfig is
used without unnecessary casts; ensure the assertion still reads
expect(badFn(async () => {})).not.toThrow(Error) and run typesafe checks for
badFn and PathRewriteConfig.
src/types.ts (1)

61-65: Inconsistent return types between sync and async path rewrite functions.

The sync function variant allows string | undefined return, but the async variant requires Promise<string>. Given that applyPathRewrite in src/http-proxy-middleware.ts (line 218) handles undefined with if (typeof path === 'string'), the async variant should likely also allow returning undefined:

♻️ Proposed fix for consistent return types
 export type PathRewriteConfig<TReq extends http.IncomingMessage = http.IncomingMessage> =
   | { [regexp: string]: string }
   | ((path: string, req: TReq) => string | undefined)
-  | ((path: string, req: TReq) => Promise<string>);
+  | ((path: string, req: TReq) => Promise<string | undefined>);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types.ts` around lines 61 - 65, The PathRewriteConfig type is
inconsistent: the synchronous function variant allows returning string |
undefined but the async variant forces Promise<string>; update the async variant
to Promise<string | undefined> so applyPathRewrite's logic (which checks if
typeof path === 'string') works for both sync and async rewrites; modify the
union variant in the PathRewriteConfig declaration to use ((path: string, req:
TReq) => Promise<string | undefined>) and keep references to PathRewriteConfig
and applyPathRewrite to locate the change.
src/path-rewriter.ts (1)

33-45: Type signature inconsistency: rewritePath should accept req parameter for consistency.

The rewritePath function only accepts path: string, but applyPathRewrite in src/http-proxy-middleware.ts (line 216) calls the rewriter with two arguments: pathRewriter(req.url as string, req).

While JavaScript silently ignores extra arguments so this works at runtime, the inconsistent signature creates a confusing union return type. Consider adding the unused req parameter for type consistency:

♻️ Proposed fix for signature consistency
-  function rewritePath(path: string) {
+  function rewritePath(path: string, _req?: TReq) {
     let result = path;

     for (const rule of rulesCache) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/path-rewriter.ts` around lines 33 - 45, The rewritePath function
signature is inconsistent with how it's invoked (applyPathRewrite calls
pathRewriter(req.url as string, req)); update rewritePath to accept the second
parameter (e.g., req: IncomingMessage | Request | any) even if unused, so the
type signature matches callers and avoids union return type confusion. Edit the
function declaration for rewritePath to add the req parameter, keep the
internals using only the path and rulesCache/regex logic, and ensure any
exported/typed declarations referencing rewritePath are updated to the new
(path, req) signature for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/handlers/response-interceptor.ts`:
- Around line 123-127: The handler is assuming header values are always present
and casts value to string[] before operating on them, which can throw if value
is undefined (notably for 'set-cookie'); update the logic around the value
variable used before response.setHeader so you first check for undefined and
only process when value is defined, normalize string into an array safely (e.g.,
if value is a string convert to [value]), then map over the array calling
replace (or skip elements that are undefined), and finally call
response.setHeader(key, value) with the guarded/normalized readonly string[];
reference the value variable, the response.setHeader call, and the 'set-cookie'
header handling in your fix.
- Around line 109-110: The assignment currently uses unsafe casts for
originalResponse.statusCode/statusMessage; replace them with type guards that
only assign when values are defined and of the correct primitive type (e.g.,
check typeof originalResponse.statusCode === 'number' before setting
response.statusCode, and typeof originalResponse.statusMessage === 'string'
before setting response.statusMessage) so you don't overwrite response with
undefined or invalid values; keep the variables response and originalResponse
and update the logic where they are set in response-interceptor.ts accordingly,
leaving defaults untouched when guards fail.

In `@test/e2e/test-kit.ts`:
- Line 10: The createApp signature currently accepts only RequestHandler[] which
forces casting for Express error middleware; change the parameter type to accept
both request and error handlers (e.g., update createApp(...middlewares:
Array<RequestHandler | ErrorRequestHandler>): Express) so consumers can pass
ErrorRequestHandler instances without casting; adjust the import/usage to
reference Express's RequestHandler and ErrorRequestHandler types where createApp
is defined and update any call sites/tests if needed.

---

Nitpick comments:
In `@src/path-rewriter.ts`:
- Around line 33-45: The rewritePath function signature is inconsistent with how
it's invoked (applyPathRewrite calls pathRewriter(req.url as string, req));
update rewritePath to accept the second parameter (e.g., req: IncomingMessage |
Request | any) even if unused, so the type signature matches callers and avoids
union return type confusion. Edit the function declaration for rewritePath to
add the req parameter, keep the internals using only the path and
rulesCache/regex logic, and ensure any exported/typed declarations referencing
rewritePath are updated to the new (path, req) signature for consistency.

In `@src/types.ts`:
- Around line 61-65: The PathRewriteConfig type is inconsistent: the synchronous
function variant allows returning string | undefined but the async variant
forces Promise<string>; update the async variant to Promise<string | undefined>
so applyPathRewrite's logic (which checks if typeof path === 'string') works for
both sync and async rewrites; modify the union variant in the PathRewriteConfig
declaration to use ((path: string, req: TReq) => Promise<string | undefined>)
and keep references to PathRewriteConfig and applyPathRewrite to locate the
change.

In `@test/unit/path-rewriter.spec.ts`:
- Line 161: The test assertion uses a redundant double-cast on the async lambda;
update the call to badFn to pass the async function directly (replace
badFn((async () => {}) as unknown as any) with badFn(async () => {})) so the
async function variant of PathRewriteConfig is used without unnecessary casts;
ensure the assertion still reads expect(badFn(async () =>
{})).not.toThrow(Error) and run typesafe checks for badFn and PathRewriteConfig.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 39b7d1a9-5d2d-45fe-9d5e-948a2bf1225c

📥 Commits

Reviewing files that changed from the base of the PR and between 7aea19f and 08b6a78.

📒 Files selected for processing (15)
  • src/handlers/response-interceptor.ts
  • src/http-proxy-middleware.ts
  • src/path-rewriter.ts
  • src/router.ts
  • src/types.ts
  • test/e2e/express-error-middleware.spec.ts
  • test/e2e/express-router.spec.ts
  • test/e2e/http-proxy-middleware.spec.ts
  • test/e2e/router.spec.ts
  • test/e2e/test-kit.ts
  • test/types.spec.ts
  • test/unit/path-filter.spec.ts
  • test/unit/path-rewriter.spec.ts
  • test/unit/router.spec.ts
  • tsconfig.json

@chimurai chimurai merged commit 7cf80ea into master Apr 6, 2026
21 checks passed
@chimurai chimurai deleted the refactor-strict-type branch April 6, 2026 14:06
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