Combine a branded id factory and an unknown-data decoder. The returned success must contain a new flat object with only id and title; input extras must not leak through a spread. Both strings are trimmed and blanks are rejected.
The brand’s rule is only “nonblank normalized string,” not uniqueness or database existence. A readonly payload is a TypeScript permission contract, not a runtime freeze. These distinctions will matter when event subscribers receive the decoded record.
Use about twenty minutes. Try invalid records before revealing the answer: null, missing id, blank title and a numeric title. A checker accepting the decoder signature is only one layer of evidence.
Predict, check, and run
Write the output before running. Use Check types first, then Check & run. Types disappear at runtime: explain both what the compiler checks and what the code actually does. Each editor is an independent ES module; await asynchronous work explicitly.
const original = { title: " Types ", secret: true };
const projected = { title: original.title.trim() };
console.log(Object.keys(projected).join(","));
console.log(original.title === " Types ");Enable JavaScript to edit and type-check this example.
Check your prediction
title
true Acceptance cases before you code
Write a small test plan before implementing validated branded payload. These cases extend the brief; they are not a second worked solution. Predict the expected behavior independently, then test through the public function. A good ordinary example and a good boundary example should catch different possible mistakes.
| Case to try | What your result should demonstrate |
|---|---|
| Padded nonblank id and title | Return the normalized new record |
| Null, blank title or nontext field | Reject before construction |
| Extra source property | Exclude it while leaving the source unchanged |
After the core task passes, deliberately break one rule and identify the case that notices it. Restore the implementation and explain the failure in terms of the contract, not the number of assertions. If a case requires a concept you have not recalled, revisit the relevant lesson before opening the solution.
Build it yourself
Checkpoint: Validated Payloads: practise
Write a Result-returning decoder for a readonly {id:BookId;title:string}. Use a checked id factory, validate nonblank title, and project the two fields. Print b1 Types and id,title for a valid input with extra fields. Assert null and blank title fail.
// Start from the stated contract. Add a valid call and an edge case.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
Validate the record before its fields, and construct success only after both fields pass.
Hint 2: a first step
Reuse the branded id factory; keep its assertion out of the payload decoder.
Show one solution
type Result<T> =
| {
ok: true;
value: T;
}
| {
ok: false;
message: string;
};
declare const brand: unique symbol;
type BookId = string & { readonly [brand]: "BookId" };
function idOf(value: unknown): BookId | null {
if (typeof value !== "string" || value.trim() === "") {
return null;
}
return value.trim() as BookId;
}
type Payload = {
readonly id: BookId;
readonly title: string;
};
function decode(value: unknown): Result<Payload> {
if (
typeof value !== "object" ||
value === null ||
!("id" in value) ||
!("title" in value)
) {
return { ok: false, message: "Expected record" };
}
const id = idOf(value.id);
if (
id === null ||
typeof value.title !== "string" ||
value.title.trim() === ""
) {
return { ok: false, message: "Invalid fields" };
}
return { ok: true, value: { id, title: value.title.trim() } };
}
for (const bad of [null, { id: "b1", title: " " }]) {
if (decode(bad).ok) {
throw new Error("Invalid payload accepted");
}
}
const result = decode({ id: " b1 ", title: " Types ", extra: 1 });
if (!result.ok) {
throw new Error(result.message);
}
console.log(result.value.id, result.value.title);
console.log(Object.keys(result.value).join(","));Enable JavaScript to edit and type-check this example.
b1 Types
id,titleTrace the implementation against the task before comparing styles. A passing example supports that case; use the changes below to challenge the wider contract.
Change it and test it
Assert the original object is unchanged. Try direct assignment to the returned title in the editor. Explain why that compiler error is separate from dropping extra fields at runtime.
Completion is saved in this browser only. It does not measure mastery.