Skip to content

fix(request): return string | undefined from param() when path type is any#4723

Merged
yusukebe merged 4 commits intohonojs:mainfrom
andrewdamelio:fix/param-type-safety
Mar 4, 2026
Merged

fix(request): return string | undefined from param() when path type is any#4723
yusukebe merged 4 commits intohonojs:mainfrom
andrewdamelio:fix/param-type-safety

Conversation

@andrewdamelio
Copy link
Copy Markdown
Contributor

When middleware uses Context with a default path type parameter (any), c.req.param('key') incorrectly returns string instead of string | undefined. This is because ParamKeys resolves to string, matching the first overload that returns string.

Add a new overload that detects when P is any using the 0 extends (1 & P) trick and returns string | undefined, ensuring type safety in middleware.

Fixes #3198

The author should do the following, if applicable

  • Add tests
  • Run tests
  • bun run format:fix && bun run lint:fix to format the code
  • Add TSDoc/JSDoc to document the code

exe.dev user and others added 2 commits February 16, 2026 11:44
When middleware uses Context with a default path type parameter (any),
c.req.param('key') incorrectly returns string instead of string | undefined.
This is because ParamKeys<any> resolves to string, matching the first
overload that returns string.

Add a new overload that detects when P is any using the 0 extends (1 & P)
trick and returns string | undefined, ensuring type safety in middleware.

Fixes honojs#3198
@yusukebe yusukebe changed the title fix: return string | undefined from param() when path type is any fix(request): return string | undefined from param() when path type is any Feb 17, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.46%. Comparing base (8b17935) to head (22d7bb3).
⚠️ Report is 38 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4723      +/-   ##
==========================================
+ Coverage   91.43%   91.46%   +0.03%     
==========================================
  Files         173      177       +4     
  Lines       11382    11563     +181     
  Branches     3300     3360      +60     
==========================================
+ Hits        10407    10576     +169     
- Misses        974      986      +12     
  Partials        1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@yusukebe
Copy link
Copy Markdown
Member

Hi @andrewdamelio

Is this really going to fix the issue written in #3198 (comment)?

Can you write a test and confirm this PR fixes it?

@andrewdamelio
Copy link
Copy Markdown
Contributor Author

Hi @andrewdamelio

Is this really going to fix the issue written in #3198 (comment)?

Can you write a test and confirm this PR fixes it?

Added two type-level tests to src/request.test.ts in the Param > Type describe block:

  1. param() returns string | undefined when P is any — creates a HonoRequest (simulating middleware context) and asserts via expectTypeOf
  2. param() returns string when P is a known route string — confirms existing typed-route behaviour is unaffected

@emmyasuai

This comment was marked as off-topic.

@yusukebe yusukebe changed the title fix(request): return string | undefined from param() when path type is any fix(request): return string from param() when path type is any Mar 4, 2026
@yusukebe yusukebe changed the title fix(request): return string from param() when path type is any fix(request): return string | undefined from param() when path type is any Mar 4, 2026
expectTypeOf(foo).toEqualTypeOf<number>()
const id = c.req.param('id')
expectTypeOf(id).toEqualTypeOf<string>()
expectTypeOf(id).toEqualTypeOf<string | undefined>()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There may be some discussion about updating the test, but if we accept this PR, the old test is wrong. The value of c.req.param('id') can be string | undefined.

Copy link
Copy Markdown
Member

@yusukebe yusukebe left a comment

Choose a reason for hiding this comment

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

LGTM!

@yusukebe
Copy link
Copy Markdown
Member

yusukebe commented Mar 4, 2026

Hi @andrewdamelio

I updated the type definition and the existing test. I think the behavior is good. I'll merge this. Thank you for your help.

@yusukebe yusukebe merged commit 0f49915 into honojs:main Mar 4, 2026
20 checks passed
@moroshko
Copy link
Copy Markdown

@andrewdamelio Shouldn't the roomId in this example be of type string?
It's string | undefined in hono version 4.12.8.

import { Hono } from 'hono'
import { upgradeWebSocket } from "hono/bun";

const app = new Hono()

app.get('/rooms/:roomId/ws', upgradeWebSocket((c) => {
  const roomId = c.req.param("roomId");

  return {
    onOpen() {
      console.log('WebSocket opened')
    },
  }
}));

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.

With UpgradeWebSocket, any path parameters become non-optional in TS type system

4 participants