A sample response is useful evidence, but it is a poor place to hide assumptions. Imagine receiving one ticket record with an identifier, a title and a customer email. Generating a type immediately feels productive. The risk appears later, when another record omits the email, returns a null value or introduces a status your interface never considered. This walkthrough turns that small sample into a reviewable contract proposal rather than treating inference as approval.
Begin with a question the sample cannot answer
Use a synthetic record, such as the following, in the JSON to TypeScript tool. Keep actual customer information out of the exercise. The useful question is not whether the generator recognizes a string; it is which decisions remain unproven after generation.
{"id":17,"title":"Replace charger","email":null,"status":"open"}
The observed email value supports null, not a promise that future values are always null. Likewise, one status string does not establish an allowed-status list. Write those uncertainties beside the output before changing anything. Ask the API owner whether email is nullable, optional or both, and whether the identifier is a durable integer or merely an example of a more general string key. This short question list is the first deliverable.
Use several records to expose missing properties
Next, compare two synthetic tickets: one with an email string and one with the email key missing entirely. Put them in a JSON array. The local generator combines observed array records and marks keys missing from some objects as optional. Review the resulting element type rather than copying the whole array type into a function that expects one ticket.
[{"id":17,"email":"[email protected]"},{"id":18}]
Optionality and nullability answer different questions. An absent email may mean it was not supplied; a present null may mean the value is deliberately empty. Choose a representation that matches the actual consumer behavior. For this example, an interface showing a contact button should explicitly handle both missing and null values if the proposed contract permits both. Do not make the button depend on an untested assumption about presence.
Make the runtime boundary explicit
TypeScript declarations help describe application code, but they do not inspect incoming network data by themselves. Generate a separate draft with JSON to JSON Schema, then review it as a candidate runtime boundary. The generator makes observed object keys required, which is a convenient starting point, not knowledge of your business rules. A reviewer must decide which keys are actually mandatory.
In JSON Schema, listing a property under properties does not require its presence; required does that separately. For the ticket proposal, keep id and title required if the consumer genuinely needs them, then deliberately choose the email rules. Review additional properties as another independent decision. A permissive boundary may ease additive API changes, while a strict boundary may help catch unexpected fields in a controlled internal format. Record the reason instead of accepting whichever setting a generator happened to emit.
Build a small acceptance matrix
Write four hand-made examples before approving the proposal: a complete ticket, a ticket without email, one with email set to null and one with the wrong identifier type. Label each accepted or rejected according to the intended contract, not according to whichever tool currently produces green output. This gives a future validator test an independent expected answer.
For an agreed numeric id, the string "17" should not silently become the number 17 unless conversion is explicitly part of your ingestion process. Similarly, an empty title and a missing title are separate cases. Check whether an empty string is allowed, and capture that rule if it matters to the product. Test the proposed schema in your chosen validator; the schema generator itself does not run that validation step.
Review changes as data, not as a wall of text
When another sample arrives, use JSON Pointer to inspect the exact nested value that changed. For example, a pointer to /customer/email examines a specific location without claiming to search every object. If you compare two complete example documents with JSON Patch, read the operations as sample changes. A replaced value in one record is not evidence that every response changed its shape.
Keep three artifacts together in your project: sanitized examples, the reviewed type and schema, and the acceptance matrix. Add a short note naming the unresolved rules. This makes a later discussion concrete: another developer can point to the missing-email case instead of asking what optional really meant during the original implementation.
Finish with an approval boundary
The useful stopping point is a proposal whose assumptions are visible. Confirm it with the API producer, implement the appropriate boundary validation, and run the accepted and rejected examples through that implementation. Keep wire values separate from any normalized application model. If normalization changes strings, defaults or missing fields, document that transformation as its own step.
Use the local generators to remove transcription work and reveal patterns. Keep responsibility for allowed values, required fields and compatibility decisions with the people maintaining the interface. A compact, reviewed contract plus a few discriminating examples is easier to maintain than a large declaration whose apparent precision came from a single response.



