A function can have a perfect Decoder<Book> signature and still accept negative pages or duplicate ids. The compiler checks the implementation’s declared relationships, not the complete truth of its validation algorithm.
Choose tests by boundaries: each required field missing independently, wrong primitive types, blank normalized text, zero, negative and fractional counts, NaN and Infinity, and normalized duplicate ids. For a whole-list decoder, assert that a bad second row rejects the list and reports its index.
Use a small assertion helper in the browser, or Node’s assert module in the downloaded project. Keep a deliberate mutation: remove one guard and confirm a test fails while the source still compiles. That distinguishes behavioral evidence from a syntax or typing failure.
Also check projection and nonmutation. A successful decode should return only modeled fields and leave the source unchanged. Do not make test fixtures so friendly that they never exercise the contract’s difficult parts.
Predict, run, and explain
Write the output and trace the important state changes before checking it. Then run the program. Use Check types first; execution is allowed only when the real compiler accepts the source.
function validPages(value: unknown): boolean {
return (
typeof value === "number" &&
Number.isFinite(value) &&
Number.isInteger(value) &&
value >= 0
);
}
function assert(condition: boolean): void {
if (!condition) {
throw new Error("Assertion failed");
}
}
assert(validPages(0));
for (const invalid of [-1, 1.5, NaN, Infinity, "0", null]) {
assert(!validPages(invalid));
}
console.log("7 boundaries checked");Enable JavaScript to edit and type-check this example.
Check your prediction
7 boundaries checked Target each domain rule with an independent fixture
A type-correct decoder can still accept the wrong data. Choose fixtures that distinguish the individual rules: missing fields, wrong primitives, blanks, negative counts, fractions and valid zero. A single malformed object may trigger only its first failure and leave other rules untested.
Check normalized output and source preservation on success, not only rejection on bad data. Otherwise a decoder that always rejects could pass a negative-only suite. Keep the expected answer independent of the implementation expression.
function valid(value: unknown): boolean {
return (
typeof value === "number" &&
Number.isFinite(value) &&
Number.isInteger(value) &&
value >= 0
);
}
for (const value of [0, 1, -1, 1.5, "0", Infinity]) {
console.log(valid(value));
}Enable JavaScript to edit and type-check this example.
Check your prediction
true
true
false
false
false
false Each case isolates a different reason. Zero and one also prove the predicate accepts ordinary valid inputs instead of rejecting everything.
const source = { title: " Types ", privateNote: true };
function clean(value: { title: string }): { title: string } {
return { title: value.title.trim() };
}
const result = clean(source);
if (
result.title !== "Types" ||
source.title !== " Types " ||
"privateNote" in result
) {
throw new Error("Boundary defect");
}
console.log("normalization, projection and input checked");Enable JavaScript to edit and type-check this example.
Check your prediction
normalization, projection and input checked A smaller return annotation does not prove any of these runtime properties. The assertions protect actual behavior at the boundary.
Try the rule on new inputs
Remove one predicate clause at a time and identify a fixture that fails. Restore it before the next mutation. Add a bad second row to verify error paths include the correct index.
Your turn
Runtime Boundary Tests: practise
Write validTitle(value:unknown) checking nonblank text. Assert a trimmed title is accepted and empty, whitespace-only, numeric and null inputs are rejected. Print 5 boundaries checked.
// The test should check the promised rule rather than reproduce implementation steps.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
Text type and nonblank content are separate requirements.
Hint 2: a first step
Use typeof before trim and test each invalid category independently.
Show one solution
function validTitle(value: unknown): boolean {
return typeof value === "string" && value.trim() !== "";
}
function assert(condition: boolean): void {
if (!condition) {
throw new Error("Title contract failed");
}
}
assert(validTitle(" Types "));
for (const invalid of ["", " ", 25, null]) {
assert(!validTitle(invalid));
}
console.log("5 boundaries checked");Enable JavaScript to edit and type-check this example.
5 boundaries checkedExplain why the implementation meets the stated contract. Which part is enforced by the language or checker, and which part needs behavioral evidence?
Change it and test it
Remove the trim check and confirm at least one test fails. Then test the real catalogue decoder with every missing field and a duplicate normalized id. Explain why readonly declarations do not prove that no runtime code elsewhere can mutate a shared record.
Completion is saved in this browser only. It does not measure mastery.