Conversation
Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/3ef047f5-46ab-4417-910c-9a0f5ad3ae33 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
…ependencies Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/3ef047f5-46ab-4417-910c-9a0f5ad3ae33 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/3ef047f5-46ab-4417-910c-9a0f5ad3ae33 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
robgruen
left a comment
There was a problem hiding this comment.
Let's make sure we have unit test coverage for each line of modified code here. No cheating on the tests, make them count.
…g all type code paths) Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/6bc9cc9a-da25-4432-95c7-0bbfef4dbb30 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
Added |
…ssary 'as any' cast Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/6bc9cc9a-da25-4432-95c7-0bbfef4dbb30 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
Let's run these tests against the old version as well and compare with the v4 results...want to make sure we have backwards compat. |
…iscriminated union merging Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/4704a0aa-e025-4f59-a926-867e2149526d Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
This PR is an intentional breaking change — the peerDependency is updated from What the 48 tests do verify is behavioral backwards compatibility: the TypeScript output (schema text, type names, interface shapes, union/intersection syntax, literal values, etc.) produced by the v4 code matches exactly what v3 produced for the same schema inputs. That's the meaningful "compat" guarantee here — schemas that worked before still generate the same TypeScript declarations. |
…xt for discriminated union merge Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/4704a0aa-e025-4f59-a926-867e2149526d Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Updates the TypeScript Zod integration to target Zod v4 by switching the schema introspection logic to v4 internals, raising the package’s Zod version requirements, and adding dedicated tests for Zod-backed schema emission and validation.
Changes:
- Reworked
src/zod/validate.tsto read Zod v4 schema internals and emit TypeScript from v4 schema shapes. - Added a new
zod.test.tssuite covering schema-to-TypeScript generation andcreateZodJsonValidator. - Updated package metadata and lockfiles to move the TypeScript package and the CoffeeShop Zod example onto Zod v4 ranges.
Show a summary per file
| File | Description |
|---|---|
typescript/test/zod.test.ts |
Adds new unit coverage for Zod schema emission and validator behavior. |
typescript/src/zod/validate.ts |
Rewrites Zod schema introspection and type emission for v4. |
typescript/package.json |
Updates test script and root Zod peer dependency range. |
typescript/package-lock.json |
Refreshes lockfile entries for the Zod v4 dependency changes. |
typescript/examples/coffeeShop-zod/package.json |
Bumps the example app’s Zod dependency to a v4 range. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Files not reviewed (1)
- typescript/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)
typescript/test/zod.test.ts:228
- This test locks in the wrong contract for
z.literal(null).nullis a valid TypeScript literal and valid JSON, so the schema emitter should preserve it instead of widening toany; otherwise a correct implementation would fail this suite.
it("emits 'any' for non-primitive literal values (e.g. null)", () => {
// null is a valid Literal in Zod v4 (util.Literal = string | number | boolean | bigint | null | undefined)
// but typeof null === "object", so it falls through to the "any" branch
const NullLiteral = z.literal(null);
assert.match(schemaOf("T", NullLiteral), /any/);
typescript/test/zod.test.ts:387
- This assertion doesn't actually verify memoization.
strictEqualon strings compares by value, so it will pass even ifgetSchemaText()recomputes a fresh but identical string on every call.
it("is memoized (returns the same string reference on repeated calls)", () => {
const v = createZodJsonValidator(SentimentSchema, "SentimentResponse");
assert.strictEqual(v.getSchemaText(), v.getSchemaText());
- Files reviewed: 4/5 changed files
- Comments generated: 4
| } | ||
| case "tuple": |
There was a problem hiding this comment.
Fixed in commit 962b334. Added v === null to the check before the typeof guards — JSON.stringify(null) produces "null", so z.literal(null) now correctly emits null instead of any. Updated the test that was asserting the old behaviour.
There was a problem hiding this comment.
Fixed in commit 962b334. Added v === null to the condition so JSON.stringify(null) → "null" TypeScript type, instead of falling through to "any". Updated the test accordingly.
There was a problem hiding this comment.
Fixed in b15235d. The literal case now handles null by checking v === null before the typeof check, so JSON.stringify(null) emits "null" correctly. The dead appendLiteral helper (which had the same null bug) was also removed to eliminate duplication.
| "scripts": { | ||
| "build": "tsc -p src", | ||
| "test": "npm run build && node --test tests/*.mjs", | ||
| "test": "tsc -p test && node --test out/validate.test.js out/zod.test.js", |
There was a problem hiding this comment.
Fixed in f2dcb80. tests/model.test.mjs is now included in the npm test command: node --test out/validate.test.js out/zod.test.js tests/model.test.mjs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/f54f51e8-1fe3-4d45-8f1b-d8990cff4661 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
… fix appendLiteral for null Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/0ff3a094-4044-48c6-9a86-51195d97c487 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/TypeChat/sessions/0ff3a094-4044-48c6-9a86-51195d97c487 Co-authored-by: robgruen <25374553+robgruen@users.noreply.github.com>
TypeChat's Zod integration relied on Zod v3 internals (
_def,ZodFirstPartyTypeKindenum, etc.) that no longer exist in v4. This PR updates the peer dependency to^4.0.0and rewrites the schema introspection logic insrc/zod/validate.tsto use the v4 API.API changes (
_def→_zod.def, string type discriminants)type._deftype._zod.defZodFirstPartyTypeKind.ZodObject"object"def.shape()(function)def.shape(property)def.values(array)Object.values(def.entries)def.options(broken in v3 too)def.left/def.rightdef.typedef.elementdef.value(scalar)def.values(array, supports multi-value)type._def.descriptiontype.descriptionReturn type
z.TypeOf<T[K]>(v3 resolved toany) is replaced withz.infer<T[K]> & object— necessary because v4 defaults the output type tounknown, which doesn't satisfyTypeChatJsonValidator<T extends object>.Multi-value literals
Zod v4 supports
z.literal(["a", "b", "c"])withvaluesas an array (a first-class overload ofz.literal()). The new code maps all values to a union:// z.literal(["active", "inactive", "pending"]) → "active" | "inactive" | "pending"Discriminated union handling
In Zod v4,
z.discriminatedUnion()no longer has a separate type kind (ZodDiscriminatedUnionin v3). It shares_zod.def.type === "union"with regularz.union()and both expose anoptionsarray, so the"union"branch handles both transparently.Type-safe discriminant strings
The string type discriminants used across all switch statements are now typed as
ZodTypeKind = z.core.$ZodTypeDef["type"](a public Zod v4 API). This means the compiler validates everycaselabel against the full union of legitimate Zod type kinds rather than accepting arbitrary strings.Package updates
typescript/package.json:"zod": "^3.22.4"→"^4.0.0"(peerDependencies)typescript/examples/coffeeShop-zod/package.json: same range bump in dependenciesTests
Added
typescript/test/zod.test.tswith 48 unit tests covering every code path in the modifiedvalidate.ts. The test suite is run alongside the existing TypeScript validator tests via an updatedtestscript inpackage.json.Coverage includes:
string,number,boolean,Date,undefined,null,unknown)anyfallthrough for unrecognized type kindscreateZodJsonValidator:getTypeName,getSchemaText(including memoization),validate()success/failure/path reportingNote
This is an intentional breaking change — the peerDependency moves from
^3.22.4to^4.0.0and the implementation uses v4-only APIs. The tests verify behavioral backwards compatibility: the TypeScript output (schema text, interface shapes, union/intersection syntax, literal values, etc.) produced by the v4 code is identical to what v3 produced for the same schema inputs.