Skip to content

Commit 3ffad49

Browse files
ochafikclaude
andcommitted
refactor: use .passthrough() instead of looseObject for base types
Using .passthrough() instead of z.looseObject() because: - looseObject adds [x: string]: unknown index signature to inferred type - This breaks TypeScript union narrowing (can't check 'prop' in obj) - .passthrough() allows extra properties at runtime without affecting type Note: Capability schemas still use looseObject intentionally since they need the extensibility semantic and don't participate in union narrowing. The Infer<typeof> type exports are still needed because: - spec.types.ts has index signatures for extensibility - sdk.types.ts inherits these from spec - Only schema-inferred types are clean (no index signatures) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bf03900 commit 3ffad49

File tree

2 files changed

+25
-14
lines changed

2 files changed

+25
-14
lines changed

scripts/generate-schemas.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,12 @@ function postProcess(content: string): string {
516516
}
517517

518518
/**
519-
* Transform z.record(z.string(), z.unknown()).and(z.object({...})) to z.looseObject({...})
519+
* Transform z.record(z.string(), z.unknown()).and(z.object({...})) to z.object({...}).passthrough()
520+
*
521+
* Using .passthrough() instead of looseObject because:
522+
* - looseObject adds [x: string]: unknown index signature to the inferred type
523+
* - This breaks TypeScript union narrowing (can't check 'prop' in obj)
524+
* - .passthrough() allows extra properties at runtime without affecting the type
520525
*/
521526
function transformRecordAndToLooseObject(sourceFile: SourceFile): void {
522527
// Find all call expressions
@@ -541,9 +546,10 @@ function transformRecordAndToLooseObject(sourceFile: SourceFile): void {
541546
const objectLiteral = objectArgs[0];
542547
if (!Node.isObjectLiteralExpression(objectLiteral)) return;
543548

544-
// Replace with z.looseObject({...})
549+
// Replace with z.object({...}).passthrough()
550+
// This allows extra properties at runtime but doesn't add index signature to type
545551
const objectContent = objectLiteral.getText();
546-
node.replaceWithText(`z.looseObject(${objectContent})`);
552+
node.replaceWithText(`z.object(${objectContent}).passthrough()`);
547553
});
548554
}
549555

src/generated/sdk.schemas.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const RequestParamsSchema = z
5858
.object({
5959
/** @description See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage. */
6060
_meta: z
61-
.looseObject({
61+
.object({
6262
/** @description If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. */
6363
progressToken: ProgressTokenSchema.optional().describe(
6464
'If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.'
@@ -68,6 +68,7 @@ export const RequestParamsSchema = z
6868
'If specified, this request is related to the provided task.'
6969
)
7070
})
71+
.passthrough()
7172
.optional()
7273
.describe('See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage.')
7374
})
@@ -93,13 +94,15 @@ export const NotificationSchema = z.object({
9394
/**
9495
* @category Common Types
9596
*/
96-
export const ResultSchema = z.looseObject({
97-
/** @description See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage. */
98-
_meta: z
99-
.record(z.string(), z.unknown())
100-
.optional()
101-
.describe('See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage.')
102-
});
97+
export const ResultSchema = z
98+
.object({
99+
/** @description See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage. */
100+
_meta: z
101+
.record(z.string(), z.unknown())
102+
.optional()
103+
.describe('See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage.')
104+
})
105+
.passthrough();
103106

104107
/**
105108
* @category Common Types
@@ -1991,9 +1994,11 @@ export const URLElicitationRequiredErrorSchema = JSONRPCErrorResponseSchema.omit
19911994
error: ErrorSchema.and(
19921995
z.object({
19931996
code: z.any(),
1994-
data: z.looseObject({
1995-
elicitations: z.array(ElicitRequestURLParamsSchema)
1996-
})
1997+
data: z
1998+
.object({
1999+
elicitations: z.array(ElicitRequestURLParamsSchema)
2000+
})
2001+
.passthrough()
19972002
})
19982003
)
19992004
})

0 commit comments

Comments
 (0)