feat(Schema): add UndefinedOrFromNullOr - #8168
davetorbeck wants to merge 1 commit into
Conversation
The `T | undefined` counterpart of `OptionFromNullOr`: decoding maps `null` to `undefined`, encoding maps `undefined` back to `null`. For programs that model absence as `undefined` (the `Array.find` / `Map.get` idiom) but must emit `null` on a JSON wire, where `JSON.stringify` would otherwise drop the key. Adds `SchemaTransformation.undefinedOrFromNullOr`, unit and type tests, and doc examples. Claude-Session: https://claude.ai/code/session_01XpmvW3MzCahnWN6PD1UCsc
🦋 Changeset detectedLatest commit: eba079f The changes in this PR will be included in the next version bump. This PR includes changesets to release 30 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|
|
@davetorbeck Very nice! I have been re-implementing this by hand in different projects. It would be a great first-class citizen, imo. |
|
For serialization to and deserialization from JSON, the idiomatic approach in v4 is not to worry about it at all and simply use import { Schema } from "effect"
const domainModel = Schema.UndefinedOr(Schema.FiniteFromString)
const codec = Schema.toCodecJson(domainModel)
const decode = Schema.decodeExit(codec)
const encode = Schema.encodeExit(codec)
console.log(String(encode(1))) // Success("1")
console.log(String(encode(undefined))) // Success(null)
console.log(String(decode("1"))) // Success(1)
console.log(String(decode(null))) // Success(undefined)video clip: https://x.com/GiulioCanti/status/2090409195807903924 |
|
@gcanti Thank you again for another explainer. That video was helpful. What program did you used to create it? It looked slick. |
What
Adds
Schema.UndefinedOrFromNullOr(schema)andSchemaTransformation.undefinedOrFromNullOr().The schema is
NullOr(schema)decoded toUndefinedOr(toType(schema)). Decoding mapsnulltoundefined. Encoding mapsundefinedtonull. Other values pass through in both directions. It follows theOptionFromNullOr/OptionFromUndefinedOr/OptionFromNullishOrfamily in shape, naming, JSDoc and placement.Includes a unit test with
TestSchema.Asserts(decoding, encoding, arbitrary generation), atstychetype test for the wrapper type and itsType/Encodedsides, doc examples that run underpnpm doctest, and apatchchangeset foreffect.pnpm checkforpackages/effect, the Schema unit test file,pnpm test-typesfor the Schema type tests,pnpm doctestfor both modules,oxlintanddprint checkall pass.Why
JSON has no
undefined.JSON.stringifydrops a key whose value isundefined, so a payload with a stable shape (a health probe, a status response) must carrynullfor absence on the wire. Many programs model absence asT | undefinedrather thanOption<T>, following theArray.find/Map.getidiom. Today that combination has no first-class codec:NullOrisnullon both sides,UndefinedOrisundefinedon both sides,optional/optionalKeydrop the key, andOptionFromNullOrrequiresOptionin the domain. The user-land answer is a per-field helper such asjsonNull(value)at every call site, which is exactly the kind of manual boundary code Schema exists to replace, or a hand-writtenencodeTowith two getters in every codebase. This PR provides the codec once, in the library, with the same name pattern as theOptionvariants so it is discoverable next to them.https://claude.ai/code/session_01XpmvW3MzCahnWN6PD1UCsc