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']