Skip to content

unknown → Safe Types: Runtime Validation Patterns

Stop lying with `as`. Learn production patterns to convert unknown data (fetch, localStorage, env) into safe TypeScript types with guards and assertions.

Rajesh DhimanMarch 01, 20262 min read
On this page

What you’ll learn

  • Why unknown is the correct type for external data
  • Type guards vs assertion functions (asserts)
  • A production pattern for validating API responses and storage

30‑second interview answer

When data comes from outside TypeScript (network, storage, user input), it should start as unknown. Converting unknown to a safe type requires runtime validation. Type guards (x is T) and assertion functions (asserts x is T) let you validate once at the boundary and keep the rest of the codebase type-safe without as casts.


The core truth: TypeScript can’t validate at runtime

This compiles and can still crash:

type User = { id: string; name: string };
 
const user = (await fetch("/api/user").then(r => r.json())) as User;
// If API returns { id: 123 }, your code is broken but TS is silent.

The fix is: treat external data as unknown.


Step 1: Start with unknown

const data: unknown = await fetch("/api/user").then(r => r.json());

Step 2: Validate with a type guard

type User = { id: string; name: string };
 
function isRecord(x: unknown): x is Record<string, unknown> {
  return typeof x === "object" && x !== null;
}
 
function isUser(x: unknown): x is User {
  if (!isRecord(x)) return false;
  return typeof x.id === "string" && typeof x.name === "string";
}
 
async function getUser(): Promise<User> {
  const data: unknown = await fetch("/api/user").then(r => r.json());
  if (!isUser(data)) throw new Error("Invalid API response: User");
  return data;
}

Now getUser() is safe.


Assertion functions (asserts) for better ergonomics

Sometimes you want validation but keep the value name.

function assertUser(x: unknown): asserts x is User {
  if (!isUser(x)) throw new Error("Invalid User");
}
 
const data: unknown = JSON.parse(localStorage.getItem("user") ?? "null");
assertUser(data);
// data is now User

Pattern: validate env vars once

type Env = {
  SITE_URL: string;
  MONGODB_URI: string;
};
 
function assertEnv(x: any): asserts x is Env {
  if (typeof x.SITE_URL !== "string") throw new Error("Missing SITE_URL");
  if (typeof x.MONGODB_URI !== "string") throw new Error("Missing MONGODB_URI");
}
 
const env = process.env;
assertEnv(env);
// env is Env

This is clean, and it keeps unsafe process.env usage out of your app.


Common mistakes

  • Using as T to silence errors instead of validating
  • Writing guards that only check a single field (false confidence)
  • Skipping boundary validation, then chasing bugs deep in the app

Production rule of thumb

  • External data starts as unknown.
  • Validate at the boundary.
  • Inside the app, keep types strong and casts rare.

Interview questions

  1. Q: Why is unknown better than any?

    • A: unknown forces validation before use; any disables safety.
  2. Q: What’s the difference between a type guard and an assertion function?

    • A: Guards return boolean (x is T); assertions throw if invalid and narrow the type (asserts x is T).

Quick recap

  • unknown is the correct starting point for untrusted data.
  • Use guards/assertions to validate.
  • This pattern scales across fetch, storage, and env.
Companion Book

Looking for structured, offline interview prep?

Free online lessons • Portable Amazon Kindle / Paperback

Frontend Interview Guide (2025 Edition) cover
Recommended for React & Frontend Rounds

Frontend Interview Guide (2025 Edition)

By Rajesh Dhiman

Comprehensive preparation covering React internals, Web Performance, CSS architecture, and system design questions.

View on Amazon ↗Kindle & Paperback
JavaScript Interview Guide cover
Recommended for Core JS Rounds

JavaScript Interview Guide

By Rajesh Dhiman

Deep dive into the Event Loop, microtask draining, closures, prototypal inheritance, and asynchronous execution patterns.

View on Amazon ↗Kindle & Paperback

Note: All lessons on Frontend Interview remain completely free to read online. As an Amazon Associate, we may earn from qualifying purchases.

RD

Written by Rajesh Dhiman

Authored by Rajesh Dhiman and engineered at Eunix Tech. Focused on deep-dive frontend engineering, JavaScript runtime internals, React architecture, and web performance.