Validate unknown JSON with runtime type guards

TypeScript
// Narrow untrusted JSON step by step; no schema library required.
type User = { id: number; name: string; email?: string };

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function isUser(value: unknown): value is User {
  if (!isRecord(value)) return false;
  if (typeof value.id !== 'number' || !Number.isSafeInteger(value.id)) return false;
  if (typeof value.name !== 'string' || value.name.length === 0) return false;
  if (value.email !== undefined && typeof value.email !== 'string') return false;
  return true;
}

function decodeUsers(payload: unknown): User[] {
  if (!isRecord(payload) || !Array.isArray(payload.users)) {
    throw new TypeError('expected an object with a users array');
  }
  const invalid = payload.users.findIndex((item) => !isUser(item));
  if (invalid !== -1) throw new TypeError(`users[${invalid}] is not a valid user`);
  return payload.users.filter(isUser);
}

const raw: unknown = JSON.parse('{"users":[{"id":1,"name":"Ada"}]}');
console.log(decodeUsers(raw).map((user) => user.name)); // ['Ada']