Add support for Temporal types#352
Conversation
Generalize the per-case skip flag so tests can opt out for reasons other than Node 10.
| // Can not define temporalConstructorToName within global scope | ||
| // as Temporal may be undefined (don't introduce breaking changes). | ||
| // So lazily initialize array | ||
| let temporalConstructorToName: Record<string, TemporalConstructor> | undefined; |
There was a problem hiding this comment.
Inverted variable name and misleading comment
temporalConstructorToName maps name → constructor (a Record<string, TemporalConstructor>), which is the opposite of what the name implies. The comment above it also says "lazily initialize array" but this is a plain object/Record. A future reader looking for how a constructor maps to its name will be confused. Consider renaming to nameToTemporalConstructor (or just temporalConstructors) and updating the comment.
| // Can not define temporalConstructorToName within global scope | |
| // as Temporal may be undefined (don't introduce breaking changes). | |
| // So lazily initialize array | |
| let temporalConstructorToName: Record<string, TemporalConstructor> | undefined; | |
| // Lazily initialized to avoid referencing Temporal at module load time on | |
| // runtimes that don't support it. | |
| let nameToTemporalConstructor: Record<string, TemporalConstructor> | undefined; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/transformer.ts
Line: 260-263
Comment:
**Inverted variable name and misleading comment**
`temporalConstructorToName` maps *name → constructor* (a `Record<string, TemporalConstructor>`), which is the opposite of what the name implies. The comment above it also says "lazily initialize array" but this is a plain object/Record. A future reader looking for how a constructor maps to its name will be confused. Consider renaming to `nameToTemporalConstructor` (or just `temporalConstructors`) and updating the comment.
```suggestion
// Lazily initialized to avoid referencing Temporal at module load time on
// runtimes that don't support it.
let nameToTemporalConstructor: Record<string, TemporalConstructor> | undefined;
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Mirrors earlier constructorToName
|
In case anyone comes across this without it being implemented, you can achieve the same functionality by doing const temporalTypes = [
Temporal.Instant,
Temporal.ZonedDateTime,
Temporal.PlainDateTime,
Temporal.PlainDate,
Temporal.PlainTime,
Temporal.PlainYearMonth,
Temporal.PlainMonthDay,
Temporal.Duration,
] as const;
for (const Type of temporalTypes) {
superjson.registerCustom<InstanceType<typeof Type>, string>(
{
isApplicable: (v): v is InstanceType<typeof Type> => v instanceof Type,
serialize: (v) => v.toString(),
deserialize: (v) => Type.from(v as never) as InstanceType<typeof Type>,
},
`Temporal.${Type.name}`,
);
}If you are sending between separate servers and clients, make sure you register the custom encoders on both |
Adds serialization support for all eight Temporal types:
Instant,Duration,PlainDate,PlainDateTime,PlainMonthDay,PlainTime,PlainYearMonth,ZonedDateTime.Values are encoded as their ISO / RFC 9557 format string form and restored via each
constructor's
.from(). Temporal globals are referenced lazily, soruntimes without Temporal are unaffected.
Also generalizes the test
skipOnNode10flag into a reusableskippredicate to gate Temporal cases on runtime support.
Test plan
npm testpassesnpm run test:temporalpasses on a Temporal-capable Nodenpm run buildsucceedsGreptile Summary
Adds first-class serialization support for all eight TC39
Temporaltypes by introducing a lazy-initializedtemporalRuleintransformer.tsand correspondingisTemporaltype guards inis.ts. Temporal globals are only accessed at call time, so runtimes without Temporal are unaffected. The test suite is well-structured with gated cases for all eight types, and theskipgeneralization cleanly replaces the oldskipOnNode10flag.Confidence Score: 5/5
Safe to merge — all remaining findings are P2 style suggestions.
The core Temporal serialization/deserialization logic is correct: lazy init guard is safe for non-Temporal runtimes, toString()/from() round-trips are correct for all 8 types, annotation lookup via constructor name is consistent, and test gating with skip: !hasTemporal works correctly. The only open item is a P2 suggestion to use NODE_OPTIONS instead of a hardcoded internal vitest path.
package.json — test:temporal script uses a vitest-internal file path that could break on upgrade.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Input value] --> B{isTemporal?} B -- Yes --> C[annotation: temporal + constructor.name] C --> D[transform: t.toString()] D --> E[JSON string e.g. 2024-01-01T10:00:00Z] E --> F{untransformValue type === temporal?} F -- Yes --> G[getTemporalConstructors] G --> H{Temporal defined?} H -- No --> I[throw: Temporal not available] H -- Yes --> J[Lazy-init name to ctor map] J --> K[lookup ctor by annotation name] K --> L[ctor.from serializedString] L --> M[Restored Temporal instance] B -- No --> N[other rules / pass through]Prompt To Fix All With AI
Reviews (2): Last reviewed commit: "fix: address scary Greptile style concer..." | Re-trigger Greptile