Create a typed parser for an unknown registration payload and represent success and failure with a discriminated union.
A fictional registration endpoint receives unknown JSON values. A valid payload has a nonblank string name and an integer seats value from 1 to 4. Type assertions alone cannot validate incoming runtime data. The local exercise sends nothing to a server.
| Payload | Expected result |
|---|---|
| {"name":"Ada","seats":2} | valid |
| {"name":"Ben","seats":"2"} | invalid: string seats |
| {"name":"","seats":1} | invalid: blank name |
| null | invalid: not object |
TypeScript can detect inconsistencies in typed code, but external input still arrives at runtime. Treat parsed JSON as unknown and validate the properties you need. Do not use any to silence a boundary problem. This case is deliberately small so the relationship between checks and returned types is visible.
Define Registration and ParseResult without using any.
Type definitions
Record your result and evidence here or in a separate file.
Success and failure branches have different explicit discriminants.
Replace an unchecked cast with a runtime condition.
Check for a non-null object before accessing properties. Then validate each property’s type and allowed range. Arrays are objects too, so explicitly reject them for this contract. Return a structured error instead of throwing for routine invalid input, and avoid including sensitive raw payloads in messages.
Implement parseRegistration(value: unknown).
Boundary parser
Record your result and evidence here or in a separate file.
All four supplied cases return the expected branch without crashing.
Test null and arrays before property access.
A discriminated union lets callers use validated data only in the success branch. A reusable result type can express the same pattern across parsers without weakening the boundary. Keep generics focused on a real repeated relationship; extra abstraction is not automatically safer or clearer.
Write a consumer that formats valid seats and displays an error otherwise.
Typed consumer
Record your result and evidence here or in a separate file.
The compiler rejects access to data in the failure branch.
Narrow on the discriminant before reading branch-specific fields.
Compiler checks and runtime tests answer different questions. Run strict compilation and execute cases for zero, one, four, five, fractional seats, arrays and null. Record the actual toolchain used. A passing compiler alone does not prove that malformed JSON is refused.
Run the supplied checks and add a missing-property case.
Compile and runtime evidence
Record your result and evidence here or in a separate file.
Boundary values 1 and 4 pass; out-of-range and non-integer values fail.
Name the expected runtime result separately from the expected compiler result.
A parser contract should explain normalization, ignored properties and error behavior. This case trims the name and accepts only the two validated fields in the returned value. Extra input properties are ignored rather than copied. State that this is validation for a local example, not complete API security.
Write the input contract and an example caller.
Parser handoff
Record your result and evidence here or in a separate file.
The returned object does not blindly spread unknown input fields.
Construct the validated output explicitly.
Deliver types, parser, consumer, strict compiler settings, runtime cases and README. Include one intentionally invalid caller in a separate compile-fail example and explain the error.
No unchecked assertion or any bypasses the external-data boundary.
| Criterion | Points | Evidence |
|---|---|---|
| Evidence and correctness | 30 | Calculations, source use and results match the supplied case; limitations are honest. |
| Required artifacts | 30 | All five stage outputs and the capstone handoff are present and inspectable. |
| Reasoning and decisions | 20 | Choices, assumptions, alternatives and the case-specific critical condition are explained. |
| Reproducibility | 10 | A reviewer can follow the stated steps and distinguish expected from observed results. |
| Clear communication | 10 | Labels, units, structure and evidence references make the work easy to review. |
Score at least 80/100, supply every required artifact, and satisfy the path-specific critical condition. Rework missing evidence before marking complete.
The string "2" is rejected rather than coerced. A valid input {name:" Ada ", seats:2} becomes {name:"Ada", seats:2}. null and arrays fail the object contract. A success result exposes data; a failure exposes an error and no data field.
This is a local type-and-validation exercise, not authentication, authorization or a deployed API.