TypeScript skill practice
How do you approach this?
TypeScript Code Tracing Practice for Developers
Published · Updated
Published by Unrust. Scoring and editorial method
Quick answer
Trace TypeScript code by following a concrete runtime value through branches, mutations, promises, and function calls while noting which type narrowing applies at each point. The compiler can tell you which operations are permitted after a guard, but it does not show which branch a real request takes or whether another reference observes a mutation. Combine the declared model with a compact state trace and a runtime check.
Original example
Trace a narrowed union without forgetting later state
type Job = { kind: "queued"; attempts: number } | { kind: "done"; result: string };
function summary(job: Job) {
if (job.kind === "queued") return `retry #${job.attempts}`;
return job.result;
}This original example gives the type narrowing a clear shape, but the tracing question is still runtime-oriented: which Job arrived, was it transformed before this call, and can a shared object be modified elsewhere? The declared union helps organize the trace; it does not remove the need to observe real state at a boundary.
Write the active union member at each branch
For discriminated unions and optional fields, record both the runtime value and the narrowed type after each meaningful condition. This prevents a trace from assuming that a branch is exhaustive when a malformed external value, a default, or a later mutation can still alter what downstream code sees.
Follow references through typed objects
An interface does not make an object immutable. Mark when two variables refer to the same object and whether a helper mutates it, returns a replacement, or stores it for later use. Types clarify the expected shape; the trace clarifies the lifetime and observable side effects.
Check a concrete outcome
Use the original example with this input. These are local teaching fixtures, checked against the displayed snippet; they do not call external services.
The discriminant selects a branch even when attempts is zero. Each branch reads only the properties of its narrowed variant.
| Expression after the example | Observed result |
|---|---|
| [summary({kind: "queued", attempts: 0}), summary({kind: "done", result: "ok"})] | ["retry #0", "ok"] |
Checklist
- What narrowed TypeScript type is active after this guard?
- Which object references remain shared across the next call?
- What runtime observation would disprove the current state trace?
Practice loop
- 01Choose one union input and record the narrowed TypeScript type after each conditional.
- 02Draw shared references separately from values that are newly constructed or copied.
- 03Compare the predicted branch and state with a debugger or a focused runtime assertion.
Limits of this page
A type-aware trace still omits framework scheduling, network timing, generated code, and values not represented in the local example. A one-off attempt is a bounded snapshot and cannot establish a general ability or explain why a particular union or data path was unfamiliar.
Source context
Published by Unrust as practical teaching material using public language documentation and original examples. See the scoring and editorial method for scope, checking and limitations.
Check your own starting point
One diagnostic session is a snapshot, not causal proof of why a result occurred. It can help you choose a focused next practice step without making a broader claim about your ability.
Take the free diagnosticTake the read in TypeScript →Same six skills, side by side on your dashboard.