by TypeGen (LX AI)
Most teams receive data before they receive a schema. A partner sends one example response. A warehouse export lands as CSV. A product manager pastes a payload into Slack and asks for "types by Friday." Hand-writing interfaces from a single sample works once; it fails when nullability, unions, and optional keys appear only in production traffic.
Key takeaways
OpenAPI and JSON Schema are excellent when they exist. Many integrations never ship them. Mobile clients evolve field by field. Legacy ETL jobs emit stringly-typed columns. In those environments, the fastest path is to take a representative sample and infer a TypeScript shape you can refine.
The risk is under-sampling. If every row in your CSV has a numeric age, you will infer number even when a rare empty cell should be number | null. If a nested meta object is missing from the sample, you will omit it entirely. Inference tools accelerate the first draft; they do not replace contract tests or human review.
Prefer a real production-like payload over a toy fixture. If you only have one object, ask for three: a typical case, a sparse case, and a failure or partial case. For CSV, include header rows plus at least a few dozen data rows that cover known enum values.
Strip secrets before pasting. Tokens, passwords, and personal identifiers do not belong in a typing tool. Redact them or replace with placeholders that preserve type shape.
Normalize encoding early. UTF-8 BOM, Windows line endings, and mixed quote styles in CSV can confuse parsers and produce phantom columns. Fix that in your editor before inference.
Interfaces fit public module boundaries and declaration merging patterns. They read cleanly in docs and IDE hover cards. Prefer them when other packages will import the shape.
Type aliases fit unions, mapped types, and utility compositions. Use them when the shape is a one-off pipeline type rather than a reusable contract.
Zod schemas fit runtime validation at trust boundaries — HTTP handlers, job consumers, and form parsers. A Zod schema can generate TypeScript types via z.infer, which keeps parse-time and compile-time aligned. Do not invent a Zod layer if your team already validates elsewhere and only needs compile-time types.
Pick the style your reviewers already expect. Switching styles mid-PR burns more time than a slightly imperfect first draft.
Paste the sample into TypeGen, select the style, and run. A useful result has three parts: the type definitions, an assumption list, and ambiguity flags. Ignore any tool that only dumps types without explaining why a field became optional or a union.
Walk the assumption list like a checklist. Did the tool treat missing keys as optional? Did it widen numbers that look like IDs into string | number? Did it collapse heterogeneous arrays into unknown[]? Each assumption is a decision you own.
Ambiguity flags are the high-value output. Mixed types in one field, nullable booleans, open-ended maps, and empty arrays are classic traps. Resolve them with domain knowledge, not more guessing.
"1" and 1 for legacy IDs).TypeScript structural typing means a wrong type can still compile if callers are loose. Treat inferred types as contracts you intend to enforce, not as decorative annotations.
CSV has no nested objects. Nested structures often appear as JSON strings inside cells, or as flattened columns like address.city. Decide whether to keep flat keys or reconstruct nested interfaces. Inference cannot invent your product model; it can only reflect the columns you fed it.
Empty cells are not the same as missing columns. Some exporters omit trailing empties; others emit blank strings. Decide whether blank means null, undefined, or empty string, and encode that decision in the type and in any runtime parse.
Date columns arrive as strings. Inferring Date is convenient and often wrong for APIs that round-trip ISO strings. Prefer string at the boundary and convert intentionally.
Heterogeneous arrays ([1, "x", null]) force unions or unknown. Prefer narrowing with discriminators when the domain has them.
Maps with dynamic keys (Record<string, T>) hide required fields. If you know a fixed set of keys, prefer an interface. If keys are truly open, document that callers must iterate rather than destructure.
Deep optional chains (a?.b?.c) often signal incomplete samples. Ask for a fuller payload before freezing the type.
Use TypeGen when you need a fast first draft from messy samples. Keep OpenAPI generators when a living contract exists. Keep hand-written types when the domain model is the product, not a mirror of an external payload.
Demo mode exists for exploration without a live model key. Live runs are labeled Model-assisted. Missing configuration and upstream failures fail closed instead of inventing a silent fake success. That honesty matters when you are about to paste types into production code.
TypeGen does not guarantee 100% type correctness. It will not swear you will never miss an ambiguous field. It is not legal advice and not a substitute for security review of payloads that may contain personal data. Outputs are decision-support for engineers who still own the merge.
If your compliance process requires a signed schema certification, use the appropriate standards body and legal counsel. A typing assistant is not that process.
With that discipline, sample-driven typing becomes a time saver instead of a source of silent production bugs. Start with a real payload, choose the style your repo already speaks, and keep humans in the loop.
Suppose a billing partner sends a webhook with id, status, amount_cents, and an optional failure object. Your first sample includes only a successful payment, so inference produces a required status: string and omits failure. That draft is useful and incomplete.
Request a second sample for a failed payment. Re-run. Now you likely see failure?: { code: string; message: string } or a union on status. The second run teaches more than polishing the first draft by hand from memory.
Name the resulting type after the domain event (BillingWebhookV1), not after the tool that produced it. Keep version suffixes when partners ship breaking changes. Attach a short comment linking to the samples you used and the date you verified them.
If amount_cents sometimes arrives as a string in older partner versions, decide explicitly: coerce at the boundary, or model number | string temporarily with a deprecation plan. Inference can surface the conflict; only your team can choose the policy.
A CRM export might include email, plan, mrr, and churned_at. Empty churned_at cells are common for active customers. Inferring a required string would force every active row through awkward sentinel values. Prefer string | null or optional string after you confirm the exporter's empty-cell rule.
Currency columns labeled mrr are often decimal strings with locale separators. TypeScript number is a compile-time wish, not a parser. Keep string at ingest if you have not yet chosen a decimal library, then convert in a dedicated function covered by tests.
Enum-like columns such as plan benefit from literal unions once you know the closed set. If sales can invent new plan codes without engineering notice, keep string and validate allowlists elsewhere. Do not freeze a literal union that product will outgrow next quarter.
Ask reviewers to challenge optionality first. Optional fields that are actually always present create needless null checks. Required fields that are sometimes absent create runtime crashes. Sample inference tends to bias toward whatever the sample showed; reviewers should call that bias out.
Ask reviewers to challenge naming. Inferred names mirror JSON keys, which are often snake_case or vendor jargon. Mapping to your domain vocabulary at the boundary keeps the rest of the codebase clean.
Ask reviewers to challenge widenings. Every unknown, any, and broad union is a debt instrument. Prefer temporary widenings with TODO tickets over silent permanent looseness.
You can call the same tool endpoint from a script that watches a fixtures folder. That is fine for regenerating drafts. It is not fine to auto-merge regenerated types without a diff review. Treat automation as a draft factory, not as a merge bot.
When AI is unavailable, scripts should fail loudly — the same 503/502/429 semantics as the UI. A CI job that invents types during an outage is worse than a red build.
Quota limits exist so fair-use stays predictable. If a pipeline needs higher volume, move to Pro or Enterprise rather than hammering free limits until the matrix returns 429.
Inference is a lever. Used with representative samples, clear style choice, and disciplined review, it compresses hours of typing into minutes of judgment. Used as a substitute for contracts and tests, it hides uncertainty behind pretty syntax. TypeGen optimizes for the first path and refuses to market the second.
OpenAPI and JSON Schema are excellent when they exist. Many integrations never ship them. Mobile clients evolve field by field. Legacy ETL jobs emit stringly-typed columns. In those environments, the fastest path is to take a representative sample and infer a TypeScript shape you can refine.
Prefer a real production-like payload over a toy fixture. If you only have one object, ask for three: a typical case, a sparse case, and a failure or partial case. For CSV, include header rows plus at least a few dozen data rows that cover known enum values.
Interfaces fit public module boundaries and declaration merging patterns. They read cleanly in docs and IDE hover cards. Prefer them when other packages will import the shape.
Paste the sample into TypeGen, select the style, and run. A useful result has three parts: the type definitions, an assumption list, and ambiguity flags. Ignore any tool that only dumps types without explaining why a field became optional or a union.
TypeScript structural typing means a wrong type can still compile if callers are loose. Treat inferred types as contracts you intend to enforce, not as decorative annotations.