Build a stock gateway independently. Its transport returns Promise<unknown>. A valid payload is an array of records with a nonblank sku:string and finite nonnegative integer stock:number. Normalize sku before checking uniqueness. A single invalid row rejects the whole result.
Return Result<number>: success is the total stock, including valid zeros; failure has path and message. Transport rejection uses path transport. Non-list data uses list. A row problem uses an index-prefixed path such as 1.stock or 1.sku. Do not coerce numeric strings or mutate input data.
Test a normal payload, empty input, zero stock, text/negative/fractional stock, normalized duplicate sku, null and transport rejection. Types should enforce the async and result contracts; runtime tests must verify the data rules. Try the implementation before revealing the worked answer.
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.
const stocks: readonly number[] = [0, 5];
let total = 0;
for (const stock of stocks) {
total += stock;
}
console.log(total);Enable JavaScript to edit and type-check this example.
Check your prediction
5 Your turn
On Your Own: Stock Gateway: practise
Implement stockTotal(transport) with the contract above. Print 5 for valid stock, 1.stock for a bad second row, and transport for a rejected transport. Also include runtime assertions for empty, duplicate, null, negative, fractional and text-stock cases.
// Keep the transport catch narrow; validate the resulting unknown list afterward.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
Uniqueness must use normalized sku, not raw input spelling.
Hint 2: a first step
Track seen sku in Set<string>, accumulating only after each row has passed every check.
Show one solution
type Result<T> =
| {
ok: true;
value: T;
}
| {
ok: false;
issue: {
path: string;
message: string;
};
};
async function stockTotal(
transport: () => Promise<unknown>
): Promise<Result<number>> {
let value: unknown;
try {
value = await transport();
} catch {
return { ok: false, issue: { path: "transport", message: "Unavailable" } };
}
if (!Array.isArray(value)) {
return { ok: false, issue: { path: "list", message: "Expected array" } };
}
let total = 0;
const seen = new Set<string>();
for (const [index, element] of value.entries()) {
const row: unknown = element;
if (
typeof row !== "object" ||
row === null ||
!("sku" in row) ||
typeof row.sku !== "string" ||
row.sku.trim() === "" ||
seen.has(row.sku.trim())
) {
return {
ok: false,
issue: { path: `${index}.sku`, message: "Invalid or duplicate sku" }
};
}
if (
!("stock" in row) ||
typeof row.stock !== "number" ||
!Number.isFinite(row.stock) ||
!Number.isInteger(row.stock) ||
row.stock < 0
) {
return {
ok: false,
issue: { path: `${index}.stock`, message: "Invalid stock" }
};
}
seen.add(row.sku.trim());
total += row.stock;
}
return { ok: true, value: total };
}
async function expectIssue(value: unknown, path: string): Promise<void> {
const result = await stockTotal(async () => value);
if (result.ok || result.issue.path !== path) {
throw new Error("Expected issue " + path);
}
}
const empty = await stockTotal(async () => []);
if (!empty.ok || empty.value !== 0) {
throw new Error("Empty contract failed");
}
for (const stock of [-1, 1.5, "0", Infinity]) {
await expectIssue([{ sku: "a", stock }], "0.stock");
}
await expectIssue(
[
{ sku: "a", stock: 0 },
{ sku: " a ", stock: 1 }
],
"1.sku"
);
await expectIssue(null, "list");
const good = await stockTotal(async () => [
{ sku: "a", stock: 0 },
{ sku: "b", stock: 5 }
]);
const bad = await stockTotal(async () => [
{ sku: "a", stock: 0 },
{ sku: "b", stock: "5" }
]);
const failed = await stockTotal(async () => {
throw "offline";
});
console.log(good.ok ? good.value : good.issue.path);
console.log(bad.ok ? "unexpected" : bad.issue.path);
console.log(failed.ok ? "unexpected" : failed.issue.path);Enable JavaScript to edit and type-check this example.
5
1.stock
transportExplain 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
Add nonmutation and missing-field tests. Temporarily remove the normalized duplicate check and confirm a test fails. Then decide whether your domain should reject totals exceeding Number.MAX_SAFE_INTEGER: per-row integer validity does not guarantee aggregate precision, so add an explicit bound if exact large counts are required.
Completion is saved in this browser only. It does not measure mastery.