Skip to content
Chapter 9Lesson 4

Quiz - JSON, classes, and the Temporal pivot

Quiz progress

0 / 0

A Server Action receives this JSON body and reads data.amount to charge a customer:

const raw = await req.text();
const data = JSON.parse(raw);
return chargeInvoice(data.invoiceId, data.amount);

What is wrong with this shape?

JSON.parse returns any by convention; the value must be typed unknown and narrowed by a Zod schema before any field is read.

The SyntaxError from JSON.parse isn’t caught at the call site — wrap the parse in a try/catch and return a 400 from inside the catch.

req.text() is the wrong method — use req.json() so the runtime parses and types the body for you.

Which of these JSON.stringify outputs are correct? Select all that apply.

JSON.stringify({ a: 1, b: undefined })'{"a":1}'

JSON.stringify([1, undefined, 3])'[1,null,3]'

JSON.stringify({ id: 42n })'{"id":"42"}'

JSON.stringify({ ratio: NaN })'{"ratio":null}'

Which of these declarations earn a class on the 2026 SaaS stack? Select all that apply.

A UserService grouping getUser, listUsers, and requireUser as static methods.

A BillingClient wrapping the Stripe SDK to hide the vendor type and centralize the apiKey and apiVersion.

A ValidationError extends Error with a literal name discriminant and a cause passthrough.

A User record paired with a formatName(user) function exported from the same module.

A teammate writes this class to hold a Stripe API key:

class SecretHolder {
private secret = 'sk_live_xxx';
}

What’s the senior critique?

TypeScript’s private is erased at compile time — the field is reachable at runtime via (holder as any)['secret'] or reflection. Use #secret for runtime-enforced privacy.

The field should be readonly so it can’t be reassigned after construction.

Hardcoded secrets belong in environment variables — move the value to process.env.STRIPE_KEY and the class is fine.

Match each scenario to the Temporal type a 2026 SaaS senior would reach for.

  1. An invoice’s due date
  2. The exact moment a Stripe webhook arrived at the route handler
  3. “Every Monday at 9 AM in the user’s timezone” recurring job
  4. The 30-day SLA window used in arithmetic

1 — Temporal.PlainDate, 2 — Temporal.Instant, 3 — Temporal.ZonedDateTime, 4 — Temporal.Duration

1 — Temporal.Instant, 2 — Temporal.ZonedDateTime, 3 — Temporal.PlainDateTime, 4 — Temporal.Duration

1 — Temporal.PlainDate, 2 — Temporal.ZonedDateTime, 3 — Temporal.PlainDateTime, 4 — Temporal.Instant

A teammate adds this to lib/billing/stripe.ts:

import { Temporal } from 'temporal-polyfill';
export const createdAt = (sub: Stripe.Subscription): Temporal.Instant =>
Temporal.Instant.fromEpochMilliseconds(sub.created * 1000);

What’s the senior critique?

Import Temporal from @/lib/temporal, never from temporal-polyfill directly. Two Temporal instances in the same process break instanceof and cross-instance from() silently.

Temporal.Instant.fromEpochMilliseconds is the wrong factory for Unix seconds — call Temporal.Instant.fromEpochSeconds(sub.created) instead.

The conversion belongs in lib/temporal.ts so the SDK adapter never touches Temporal at all.

True or false: new Date('2026-05-15') and new Date('2026-05-15 00:00') produce the same Date on every server.

False — the ISO date-only form parses as UTC; the space-separated form parses as the runtime’s local timezone. The two strings produce the same instant only when the server is on UTC.

True — both strings describe midnight on May 15, 2026; the parse normalizes them to the same millisecond count.

Quiz complete

Score by topic