Repository navigation
feat(emitter): load absent required nullable keys as null with = null - #347
Merged
Merged
Conversation
`x: T | null = null` is now accepted on a required nullable property. Load treats an absent key as null instead of rejecting it, while save still always writes the key (as null when unset). This models documents written before the key existed (lenient load, strict save) with stock TypeSpec, no new decorator. - IR: record `hasNullDefault` on the field and load assignment; a `null` default on any other property is still a diagnostic. - Policy: add `shouldRejectAbsentNullableKey` and route every runtime's missing-key guard (TS, Python, Go, C#, Java, Rust, Swift, inherited and Swift unknown-discriminator paths) through it; save paths are unchanged. - Coverage: extend `fixtures/features/nullable` (row scalar/model, inherited base field, all-defaulted model), IR golden in validate-fixtures, per-runtime codegen assertions, and an executable Python absent-key -> null probe. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
Materialize absent required-nullable base keys without overwriting existing values or unknown payload fields. Keep optional fields and variant-only fields unchanged. Add executable absent/null/populated conformance across ten runtimes and dedicated Swift/Codable unknown-kind red-green regressions. Scope inherited Rust coverage to typed polymorphic collections; raw single-reference forwarding is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
Keep the shape sample fully materialized for native serde object-equality round trips. The executable conformance input still omits defaulted keys and independently requires explicit nulls on save. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
Keep identical enum definitions in the first declaring file and import them elsewhere. Rust crate re-exports stay unchanged, but the owning source module may move with emission order. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
Recover the alias from the declaring variants, including nested aliases, instead of naming the enum after its property. Pin the Python import failure and alias-sharing behavior in generated output and executable conformance. Generated symbols for Alias | null now use the declared alias name (for example Mood instead of mood); consumers directly referencing the old property-derived symbol must update it. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
Generate a non-null guard with the record key path instead of dereferencing object?. Preserve non-null and empty strings; reject null entries rather than putting null in IDictionary<string, string>. Reproduced CS8602 for plain and nullable records. Both generated C# build and executable conformance pass after repair; the full emitter suite passes 931 tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
The full validation run passed runtime gates but found the newly added Combo field missing from the nullable fixture expected flags. Record its optional-nullable shape. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25c59b95-6ae9-4775-bd14-86a11b625d8d
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Add the stock TypeSpec spelling
x: T | null = nullfor legacy input that omits a key which canonical output must always write.x?: Tor `x?: TSwift and Swift/Codable unknown-discriminator carriers also fill absent inherited null-default keys on save while preserving non-null values and unknown payload fields. Required nullable fields without defaults still reject absence.
The authorized release batch also repairs shared enum declarations in Go/Rust/Swift, nullable alias names across targets, and C#
Record<string>loads that failed with CS8602. Null string-record entries now report their key path instead of dereferencing null.Fixes #346.
Reproduction-first checklist
Typra fixes codegen bugs by turning the reproduction into a permanent test. For any change to emitter
behavior or generated output, confirm:
packages/typra-emitter/fixtures/features/<area>/main.tsp), or an existing one was extended.validate-fixtures.mjsassertion, a golden undergenerated/fixtures/<lang>, and/or a unit test that fails onmainand passes here.@vector/@sampleencodes input → expected/expectedError.npm test,npm run validate:fixtures, andnpm run lintpass.type(scope): summary) — it becomes the squash commit and the CHANGELOG entry release-please generates.Notes
Commands below ran in
packages/typra-emitterunless stated otherwise.npm test: passed, 931 tests, 0 failures, 0 skipped, exit 0.npm run lint: passed, exit 0.npm run validate:fixtures: complete rerun passed at 113dc4a, exit 0. All generated-test and executable-conformance stages completed; only the seven declared formatter-idempotency stages below were deferred.npm run pack:dry-run(repository root): passed, exit 0.calm,busy, and null in Python and Pydantic. Permanent nullable fixture tests also cover a nested alias.The earlier Windows gofmt failures came from CRLF checkouts of two unchanged Go adapters. Normalizing those working copies to their indexed LF content removed the failures without a Git diff. No fixture line-ending change is included in the commits. The first full run found a stale nullable IR golden for the new
Combofield; 113dc4a corrects it and the complete rerun passes.Known limitations
BREAKING CHANGE: nullable literal aliases now use their declared type name (for example
Mood, not the property-derivedmood). Update direct references to old generated symbols when regenerating 3.0.0 output. Rust shared enum declarations may move between model source modules; use the unchanged generated module re-exports rather than an owner-file-specific import.