Skip to content

Commit f6b744e

Browse files
fix(types): annotate JSON*Schema with input type so isSpecType narrows
JSONValueSchema/JSONObjectSchema/JSONArraySchema were typed as z.ZodType<T> (output only). Zod v4's ZodType<O, I> defaults I to unknown, so z.input<typeof JSONValueSchema> was unknown and isSpecType.JSONValue was (v: unknown) => v is unknown, a no-op predicate. Annotating as z.ZodType<T, T> fixes the input type at the source (these schemas have no defaults/transforms, so input equals output). Test asserts the narrowing.
1 parent 17d75b1 commit f6b744e

2 files changed

Lines changed: 25 additions & 4 deletions

File tree

packages/core/src/types/schemas.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import type {
1212
ResultTypeMap
1313
} from './types.js';
1414

15-
export const JSONValueSchema: z.ZodType<JSONValue> = z.lazy(() =>
15+
export const JSONValueSchema: z.ZodType<JSONValue, JSONValue> = z.lazy(() =>
1616
z.union([z.string(), z.number(), z.boolean(), z.null(), z.record(z.string(), JSONValueSchema), z.array(JSONValueSchema)])
1717
);
18-
export const JSONObjectSchema: z.ZodType<JSONObject> = z.record(z.string(), JSONValueSchema);
19-
export const JSONArraySchema: z.ZodType<JSONArray> = z.array(JSONValueSchema);
18+
export const JSONObjectSchema: z.ZodType<JSONObject, JSONObject> = z.record(z.string(), JSONValueSchema);
19+
export const JSONArraySchema: z.ZodType<JSONArray, JSONArray> = z.array(JSONValueSchema);
2020
/**
2121
* A progress token, used to associate progress notifications with the original request.
2222
*/

packages/core/test/types/specTypeSchema.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@ import { describe, expect, expectTypeOf, it } from 'vitest';
33
import type { OAuthMetadata, OAuthTokens } from '../../src/shared/auth.js';
44
import type { SpecTypeName, SpecTypes } from '../../src/types/specTypeSchema.js';
55
import { isSpecType, specTypeSchemas } from '../../src/types/specTypeSchema.js';
6-
import type { CallToolResult, ContentBlock, Implementation, JSONRPCRequest, ResourceTemplateType, Tool } from '../../src/types/types.js';
6+
import type {
7+
CallToolResult,
8+
ContentBlock,
9+
Implementation,
10+
JSONObject,
11+
JSONRPCRequest,
12+
JSONValue,
13+
ResourceTemplateType,
14+
Tool
15+
} from '../../src/types/types.js';
716

817
describe('specTypeSchemas', () => {
918
it('returns a StandardSchemaV1 validator that accepts valid values', () => {
@@ -91,6 +100,18 @@ describe('isSpecType', () => {
91100
}
92101
});
93102

103+
it('JSONValue / JSONObject — narrows to the JSON type, not unknown', () => {
104+
// These schemas use an explicit z.ZodType<T, T> annotation for recursion; without the
105+
// second param Zod's Input defaults to `unknown` and the predicate would not narrow.
106+
const v: unknown = { a: 1 };
107+
if (isSpecType.JSONValue(v)) {
108+
expectTypeOf(v).toEqualTypeOf<JSONValue>();
109+
}
110+
if (isSpecType.JSONObject(v)) {
111+
expectTypeOf(v).toEqualTypeOf<JSONObject>();
112+
}
113+
});
114+
94115
it('guards work as filter callbacks and narrow the element type', () => {
95116
const mixed: unknown[] = [{ type: 'text', text: 'hi' }, 42, { type: 'text' }];
96117
const blocks = mixed.filter(isSpecType.ContentBlock);

0 commit comments

Comments
 (0)