Kodokon kodokon.com

Typing real-world code: DOM, fetch, JSON

Keep reliable types at your application's boundaries, where the DOM, the network, and JSON escape the compiler.

10 min · 3 questions

Open this lesson in Kodokon

Real-world code starts where perfect types end: the DOM, the network, and JSON return data that TypeScript cannot guess. First frontier: the DOM. querySelector returns Element | null, a type too vague to read value or call focus().

TYPESCRIPT
const input = document.querySelector<HTMLInputElement>(
  "#email"
);

if (input) {
  input.value = "test@example.com";
  input.focus();
}
A type parameter on querySelector and a guard against null

The type parameter is a promise you make to the compiler: it's up to you to target the right selector. When the element comes from an event, prefer an instanceof check, which actually verifies the type at runtime.

TYPESCRIPT
const form = document.querySelector("form");

form?.addEventListener("submit", (event) => {
  event.preventDefault();
  if (event.target instanceof HTMLFormElement) {
    console.log(new FormData(event.target));
  }
});
instanceof checks and narrows the type at the same time

Second frontier: the network. response.json() returns Promise<any>, in other words an open door to anything. The professional minimum: declare the expected type, check response.ok, and centralize the call in a typed function.

TYPESCRIPT
interface Post {
  id: number;
  title: string;
}

async function getPost(id: number): Promise<Post> {
  const url = `https://api.example.com/posts/${id}`;
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return (await response.json()) as Post;
}
A centralized, typed fetch call

That as Post remains a statement of trust: if the API changes, nothing warns you at runtime. For critical data, write a type guard that validates the actual shape of the JSON before using it.

TYPESCRIPT
function isPost(value: unknown): value is Post {
  if (typeof value !== "object" || value === null) {
    return false;
  }
  const obj = value as Record<string, unknown>;
  return (
    typeof obj.id === "number" &&
    typeof obj.title === "string"
  );
}
A type guard validating the received JSON (continuing the example)

Knowledge check

Make sure you remember the key points of this lesson.

  1. What is the return type of response.json() on a fetch response?
    • Promise<unknown>
    • Promise<any>
    • Promise<object>
    • any
  2. Why prefer event.target instanceof HTMLFormElement over an as HTMLFormElement assertion?
    • It's faster at runtime
    • instanceof actually checks the type at runtime, whereas as merely silences the compiler
    • as is forbidden in strict mode
  3. What does document.querySelector('#email') return without a type parameter?
    • HTMLInputElement
    • Element | null
    • HTMLElement
    • null