fix(json): keep contextual any and unknown serialization valid - #2254
Conversation
e8340d2 to
69dfe08
Compare
|
State of this pull request after the campaign consolidation. #2253 is not included in the consolidated #2266 and stays open here. The branch now carries the analysis and the partial work as an explicitly incomplete commit, so nothing is lost, but it must not be merged as-is. Root cause, re-derived from source. The class is wider than the reported witness. A sparse What is missing, and why this was excluded rather than shipped.
Nothing here has been compiled or executed. It was written under a maintainer stop on all local building and testing; no cell of either test has ever run. |
Claim issue #2253 before implementation so one batch owns the shared joinder consequence surface and its mutation-sensitive regression. Constraint: Campaign pull requests cannot change versions, tags, releases, or publication state. Rejected: Patch before claim | the campaign requires public ownership before product mutation. Confidence: high Scope-risk: moderate Directive: Treat the issue invariant as authoritative and every proposed emitter mechanism as a hypothesis until executed. Tested: Independent real-transform reproduction and issue critic review passed on ce88a88. Not-tested: Product implementation and integration gates are intentionally pending.
`JSON.stringify` answers `undefined` for a function, a symbol, and a
`toJSON` that returns nothing, while the value itself is present. The
emitted serializer decided omission from a test on the *input*
(`undefined === input || "function" === typeof input`), which cannot see
either of the last two, and joined array elements with
`.map(...).join(",")`, where a mapped `undefined` and a hole both render
as empty text. The result was text that is not JSON at all:
- `{"k":undefined}` for a present member with no serialization;
- `[,1]` for a sparse array, and `[undefined,1]` for a function or symbol
element;
- `{"a":1,}` when the omitted member was the last one, because the tail
helper was skipped whenever the last member was declared required —
a required `any` member is still omitted when its value has no text.
Omission is now decided from the serialized result, once. Three internal
helpers own the three positions ECMAScript treats differently:
`_jsonStringifyProperty` drops a member and its separator, while
`_jsonStringifyArray` and `_jsonStringifyElement` write `null`, because
an array position cannot be dropped without shifting what follows.
Evaluating once matters: a `toJSON` is free to answer differently on a
second call, so testing the input and then serializing is not the same
program.
The class is wider than the reported `any` and `unknown` witness and is
fixed as a class: a hole exists at runtime whatever the element type
declares, and a declared `toJSON` whose return type admits `undefined`
reaches the same state without `any` anywhere. Index-signature members
take the same decision as static ones, and tuple slots take the array
answer rather than the object one.
Verification: the transform regression executes 354 runtime cases across
direct and factory forms and all four stringify entry points, in default
and `undefined: false` modes, comparing every result against native
`JSON.stringify` as the oracle. Fail-before was observed on the same
regression, which reported malformed text for records, dynamic members,
arrays, and tuples before this change. The whole
`native/cmd/ttsc-typia` package passes.
Change integrity: `transform_helpers_test.go` gains runtime stubs for the
three new helpers. That shared rewriter fails loudly on any unmapped
`typia/lib/internal/` import, so every transform test that serializes an
array would otherwise stop resolving; the stubs mirror the shipped
helpers exactly, and the shipped helpers themselves are exercised by the
TypeScript suite added here.
Deferred, not dropped: a non-callable `toJSON` twin is rejected by the
declared type but the checker admits it and the serializer then throws,
so `isStringify` and `validateStringify` throw where their contract is to
answer. That is a callability rule rather than this contextual one, and
it is tracked separately with its own twin.
Closes #2253
69dfe08 to
2eec873
Compare
|
Implemented. This pull request now carries the fix, not the analysis. Root cause. Omission was decided from a test on the input ( Three positions, three answers, matching ECMAScript rather than one rule everywhere: Fixed as a class, not as the reported witness:
That last one was reachable without any of this issue's inputs: the tail helper was skipped whenever the last member was declared required, but a required Verification. The transform regression executes 354 runtime cases across direct and factory forms and all four stringify entry points, in default and Change integrity. Deferred, not dropped. A non-callable |
Closes #2253.
This draft reserves the independently reproduced JSON contextual-serialization defect before implementation. The product invariant is that every child value accepted through
anyorunknownfollows ECMAScript JSON context: object children that serialize to JavaScriptundefinedare omitted, while array and tuple children becomenull; successful text must parse as JSON.Implementation has not started. The batch will add real-transform regressions across direct/factory and raw/is/assert/validate families, enumerate every shared joinder consumer, inspect emitted JavaScript, and mutation-prove the permanent oracle before the prospective and exact-merge integration gates.
Campaign constraints: no package version, tag, release, publication, or unrelated schema-interface change.