Repository: honojs/hono (TypeScript)

Issue:
Path params are not inferred correctly
### What version of Hono are you using?

3.12.7

### What runtime/platform is your app running on?

Bun

### What steps can reproduce the bug?

```ts
import { Hono } from 'hono';
import { hc } from 'hono/client';

const app1 = new Hono().get('/c/:d', async (c) => c.json({ message: c.req.param('d') }));
const app2 = new Hono().route('/a/:b', app1);

const client = hc<typeof app2>('http://localhost:80');
client.a[':b'].c[':d'].$get({ param: { b: '123', d: '456' } });
```

### What is the expected behavior?

Parameter `b` must be allowed

### What do you see instead?

Parameter `b` is not allowed
<img width="925" alt="image" src="https://github.com/honojs/hono/assets/40269790/a0e17d9f-8cc0-45d9-83df-61ef8a825198">


### Additional information

_No response_


Discussion: Hi @siriscmv 

Thanks for raising the issue. This is a bug. I'll fix it later.



Fix (patch):
diff --git a/deno_dist/types.ts b/deno_dist/types.ts
index 03c64f892..3c7c4b1a1 100644
--- a/deno_dist/types.ts
+++ b/deno_dist/types.ts
@@ -3,7 +3,7 @@
 /* eslint-disable @typescript-eslint/ban-types */
 import type { Context } from './context.ts'
 import type { Hono } from './hono.ts'
-import type { IntersectNonAnyTypes, UnionToIntersection } from './utils/types.ts'
+import type { IntersectNonAnyTypes, RemoveBlankRecord, UnionToIntersection } from './utils/types.ts'
 
 ////////////////////////////////////////
 //////                            //////
@@ -1621,8 +1621,30 @@ export type Schema = {
   }
 }
 
-export type MergeSchemaPath<OrigSchema, SubPath extends string> = {
-  [K in keyof OrigSchema as `${MergePath<SubPath, K & string>}`]: OrigSchema[K]
+type ExtractParams<Path extends string> = string extends Path
+  ? Record<string, string>
+  : Path extends `${infer Start}:${infer Param}/${infer Rest}`
+  ? { [K in Param | keyof ExtractParams<`/${Rest}`>]: string }
+  : Path extends `${infer Start}:${infer Param}`
+  ? { [K in Param]: string }
+  : never
+
+export type MergeSchemaPath<OrigSchema extends Schema, SubPath extends string> = {
+  [P in keyof OrigSchema as MergePath<SubPath, P & string>]: {
+    [M in keyof OrigSchema[P]]: OrigSchema[P][M] extends {
+      input: infer Input
+      output: infer Output
+    }
+      ? {
+          input: Input extends { param: infer Params }
+            ? { param: Params & ExtractParams<SubPath> }
+            : RemoveBlankRecord<ExtractParams<SubPath>> extends never
+            ? Input
+            : Input & { param: ExtractParams<SubPath> }
+          output: Output
+        }
+      : never
+  }
 }
 
 export type AddParam<I, P extends string> = ParamKeys<P> extends never
diff --git a/src/types.ts b/src/types.ts
index 88b036b21..510478709 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -3,7 +3,7 @@
 /* eslint-disable @typescript-eslint/ban-types */
 import type { Context } from './context'
 import type { Hono } from './hono'
-import type { IntersectNonAnyTypes, UnionToIntersection } from './utils/types'
+import type { IntersectNonAnyTypes, RemoveBlankRecord, UnionToIntersection } from './utils/types'
 
 ////////////////////////////////////////
 //////                            //////
@@ -1621,8 +1621,30 @@ export type Schema = {
   }
 }
 
-export type MergeSchemaPath<OrigSchema, SubPath extends string> = {
-  [K in keyof OrigSchema as `${MergePath<SubPath, K & string>}`]: OrigSchema[K]
+type ExtractParams<Path extends string> = string extends Path
+  ? Record<string, string>
+  : Path extends `${infer Start}:${infer Param}/${infer Rest}`
+  ? { [K in Param | keyof ExtractParams<`/${Rest}`>]: string }
+  : Path extends `${infer Start}:${infer Param}`
+  ? { [K in Param]: string }
+  : never
+
+export type MergeSchemaPath<OrigSchema extends Schema, SubPath extends string> = {
+  [P in keyof OrigSchema as MergePath<SubPath, P & string>]: {
+    [M in keyof OrigSchema[P]]: OrigSchema[P][M] extends {
+      input: infer Input
+      output: infer Output
+    }
+      ? {
+          input: Input extends { param: infer Params }
+            ? { param: Params & ExtractParams<SubPath> }
+            : RemoveBlankRecord<ExtractParams<SubPath>> extends never
+            ? Input
+            : Input & { param: ExtractParams<SubPath> }
+          output: Output
+        }
+      : never
+  }
 }
 
 export type AddParam<I, P extends string> = ParamKeys<P> extends never