TypeScript unknown and user-defined type guards

JSON.parse returns any, which switches off type checking. A safer habit is to treat external data as unknown. A value of type unknown cannot be used until it is narrowed, so the compiler forces you to check it first. typeof, instanceof, and in checks narrow it step by step.

TypeScript
interface User {
  name: string;
  age: number;
}

function isUser(value: unknown): value is User {
  if (typeof value !== "object" || value === null) {
    return false;
  }
  const record = value as Record<string, unknown>;
  return typeof record.name === "string" && typeof record.age === "number";
}

function describe(json: string): string {
  let parsed: unknown;
  try {
    parsed = JSON.parse(json);
  } catch {
    return "invalid JSON";
  }
  return isUser(parsed) ? parsed.name + " is " + parsed.age : "invalid shape";
}

console.log(describe('{"name":"Ada","age":36}'));
console.log(describe('{"name":"Bo"}'));
console.log(describe("not json"));
console.log(describe("null"));
Read the full lesson