Module: TypeScript
TypeScript·154·4 MIN READ

154: TypeScript - Narrowing and Domain Models

TOPICS COVERED: TypeScript - Narrowing and Domain Models

Learning outcomes

  • use unions, literal types, predicates, and discriminated unions;
  • narrow unknown safely;
  • model valid task states without ambiguous combinations.

Study

Types should make invalid states difficult to represent, not merely decorate JavaScript. Prefer TaskStatus = "open" | "done" over arbitrary strings. Narrow external data with checks or a schema parser. A type assertion changes the compiler's belief; it does not inspect the value.

Practice

Implement parseTask(value: unknown): Task with checks for an ID, title, status, and timestamps. Return a success or validation-failure union. Test missing fields, extra fields, and wrong runtime types.

Worked example

Narrow one field at a time. Do not use Object.keys(value) as proof that the values have the right types.

ts
type Task = { id: string; title: string; status: 'open' | 'done' }
type ParseResult = { ok: true; value: Task } | { ok: false; message: string }

function parseTask(value: unknown): ParseResult {
  if (typeof value !== 'object' || value === null) return { ok: false, message: 'object required' }
  const record = value as Record<string, unknown>
  if (typeof record.id !== 'string' || typeof record.title !== 'string') {
    return { ok: false, message: 'id and title required' }
  }
  if (record.status !== 'open' && record.status !== 'done') {
    return { ok: false, message: 'invalid status' }
  }
  return { ok: true, value: { id: record.id, title: record.title, status: record.status } }
}

The assertion is local and immediately followed by checks. It does not make the whole input trusted.

Edge cases and questions

Test null, arrays, inherited properties, empty strings, a numeric status, and an object with extra fields. Explain why a discriminated result forces the caller to handle failure before using value.

Checkpoint

Explain the difference between a union, intersection, type predicate, and discriminated union. Demonstrate that malformed JSON cannot become a Task by assertion alone.

References