Introduction: JSON and TypeScript — the perfect pair for working with data
JSON (JavaScript Object Notation) is a lightweight data interchange format that has become the de facto standard for client-server communication, as well as for storing configurations and transmitting structured information. TypeScript, being a strictly typed superset of JavaScript, provides powerful tools for safe and predictable work with JSON. In this article, we will practically examine how to correctly parse (convert a JSON string into an object) and serialize (convert an object into a JSON string) data in TypeScript, avoiding common mistakes and leveraging the capabilities of the type system.
Basics: JSON.parse() and JSON.stringify() in TypeScript
TypeScript fully inherits the built-in JavaScript methods for working with JSON: JSON.parse() and JSON.stringify(). However, their use in a strictly typed environment requires a special approach.
JSON.parse() — from string to object
The JSON.parse() method takes a JSON string and returns an object. In TypeScript, the result by default has the type any, which deprives us of all the benefits of typing. Let's look at an example:
const jsonString = '{"name": "TypeScript", "version": 5.3}';const parsed: any = JSON.parse(jsonString);console.log(parsed.name); // Works, but no autocompletion or type checkingTo get a typed result, use type assertion:
interface Language { name: string; version: number;}
const jsonString = '{"name": "TypeScript", "version": 5.3}';const parsed = JSON.parse(jsonString) as Language;console.log(parsed.name.toUpperCase()); // Autocompletion works!JSON.stringify() — from object to string
JSON.stringify() serializes an object into a JSON string. TypeScript checks that the passed object is valid for serialization (does not contain functions, undefined, Symbol, etc.).
interface User { id: number; name: string; isActive: boolean;}
const user: User = { id: 1, name: "Alice", isActive: true };const jsonString = JSON.stringify(user);console.log(jsonString); // '{"id":1,"name":"Alice","isActive":true}'Advanced Techniques: Typing and Error Handling
In practice, working with JSON is rarely perfect. Data may come from the server in an unexpected format, contain extra fields, or lack required ones. Let's look at how TypeScript helps deal with these issues.
Creating a generic function for safe parsing
Let's write a universal function that takes a JSON string and returns a typed result with error handling:
function safeParse<T>(json: string): T | null { try { return JSON.parse(json) as T; } catch (error) { console.error("JSON parsing error:", error); return null; }}
// Usageinterface Product { id: number; title: string; price: number;}
const rawData = '{"id": 101, "title": "Laptop", "price": 999.99}';const product = safeParse<Product>(rawData);if (product) { console.log(product.title); // "Laptop"}Data validation using custom type guards
Even after type assertion with as T, TypeScript does not check that the data actually matches the interface. For runtime validation, use type guards:
function isProduct(obj: any): obj is Product { return ( typeof obj === 'object' && obj !== null && typeof obj.id === 'number' && typeof obj.title === 'string' && typeof obj.price === 'number' );}
const rawData = '{"id": "abc", "title": "Phone", "price": 599}';const parsed = JSON.parse(rawData);if (isProd