# TypeScript Code Tracing Practice for Developers

> Trace TypeScript by following runtime values through narrowed unions, object references, and async branches while using type declarations as hypotheses rather than execution records.

- Canonical HTML: [https://unrust.dev/languages/typescript/code-tracing](https://unrust.dev/languages/typescript/code-tracing)
- Markdown representation: [https://unrust.dev/languages/typescript/code-tracing/index.md](https://unrust.dev/languages/typescript/code-tracing/index.md)

Published: 2026-07-14

## Direct 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.

## 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.

## Original example

### Trace a narrowed union without forgetting later state

```typescript
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.

## 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

1. Choose one union input and record the narrowed TypeScript type after each conditional.
2. Draw shared references separately from values that are newly constructed or copied.
3. Compare 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

Editorially reviewed for Unrust using public language documentation, common code-review practice, and small original examples written for this resource system.

## Related resources

- [How to Trace State in Unfamiliar Code](https://unrust.dev/use-cases/trace-state-in-unfamiliar-code/index.md): Trace state in an unfamiliar function by following only the values, references, branches, and side effects that can change the behavior you are investigating.
- [How to Understand an AI-Generated Pull Request](https://unrust.dev/use-cases/understand-an-ai-generated-pull-request/index.md): Understand an AI-generated pull request by mapping the request to the diff, tracing its data flow, and isolating every assumption before approval.

Next step: [Take the free diagnostic](https://unrust.dev/diagnostic/index.md)
