Skip to main content

JSON

Introduction​

JSON ( JavaScript Object Notation ) is the universal language of data exchange on the web. It's a lightweight, text-based format that's easy for humans to read and write, and easy for machines to parse and generate.

Think of JSON as the "common language" that allows different systems to communicate:

  • Your frontend JavaScript app talks to a backend Python server? JSON
  • Saving user preferences to a file? JSON
  • Configuring your application? JSON
  • APIs sending data back and forth? JSON

JSON was derived from JavaScript, but it's now language-independent - virtually every programming language can read and write JSON.

Why JSON Matters​

Before JSON , data exchange was messy with formats like XML:

<!-- XML - verbose and complex -->
<person>
<name>Alice</name>
<age>25</age>
<isStudent>true</isStudent>
</person>

With JSON - clean and simple:

{
"name": "Alice",
"age": 25,
"isStudent": true
}

JSON won because it's:

  • Lightweight - Less data to transfer
  • Readable - Easy for humans to understand
  • Native to JavaScript - No extra parsing libraries needed
  • Universal - Every language supports it

TypeScript's JSON Type Safety​

The challenge with JSON is that it's just text . When you parse JSON, TypeScript can't automatically know what structure it has. You need to add type safety yourself:

JavaScript (no type checking):

let data = JSON.parse('{"name":"Alice","age":25}');
console.log(data.emial); // undefined - typo! No error

TypeScript (with type checking):

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

let jsonString = '{"name":"Alice","age":25}';
let data = JSON.parse(jsonString) as User; // Type assertion
console.log(data.name); // ✅ Type-safe
// console.log(data.emial); // ❌ Error: Property 'emial' does not exist

JSON Format Rules​

JSON has strict formatting rules:

Valid JSON Types​

JSON supports these value types:

  • String : "hello" (must use double quotes)
  • Number : 42, 3.14, -10, 1e10
  • Boolean : true, false
  • null : null
  • Array : [1, 2, 3]
  • Object : {"key": "value"}

What JSON CANNOT Contain​

  • Functions : Methods are not allowed
  • undefined : Use null instead
  • Dates : Must be strings (e.g., "2024-01-01")
  • Comments : No // or /* */
  • Single quotes : Must use double quotes for strings
  • Trailing commas : {"a": 1,} is invalid

JSON vs JavaScript Objects​

FeatureJavaScript ObjectJSON
KeysCan be unquotedMust be quoted strings
String quotesSingle or doubleDouble only
ValuesAny type including functionsLimited types
Trailing commasAllowedNot allowed
CommentsAllowedNot allowed

JavaScript Object:

{
name: 'Alice', // Unquoted key, single quotes
age: 25,
greet() { } // Method allowed
}

Valid JSON:

{
"name": "Alice",
"age": 25
}

JSON.stringify() - Object to JSON​

Convert JavaScript objects to JSON strings:

Basic Usage​

interface Person {
name: string;
age: number;
isStudent: boolean;
}

let person: Person = {
name: "Alice",
age: 25,
isStudent: true
};

let jsonString: string = JSON.stringify(person);
console.log(jsonString);
// '{"name":"Alice","age":25,"isStudent":true}'

console.log(typeof jsonString); // "string"

Pretty Printing with Indentation​

let person = {
name: "Alice",
age: 25,
address: {
city: "London",
country: "UK"
}
};

// JSON.stringify(value, replacer, space)
let prettyJSON = JSON.stringify(person, null, 2);
console.log(prettyJSON);
/*
{
"name": "Alice",
"age": 25,
"address": {
"city": "London",
"country": "UK"
}
}
*/

The third parameter controls indentation:

  • 2 - 2 spaces per level
  • 4 - 4 spaces per level
  • "\t" - Tab character

What Gets Omitted​

let obj = {
name: "Alice",
age: 25,
greet() { return "Hello"; }, // Function - omitted
password: undefined, // undefined - omitted
email: null // null - kept!
};

console.log(JSON.stringify(obj));
// '{"name":"Alice","age":25,"email":null}'

Arrays​

let numbers: number[] = [1, 2, 3, 4, 5];
console.log(JSON.stringify(numbers)); // '[1,2,3,4,5]'

let mixed: any[] = [1, "hello", true, null];
console.log(JSON.stringify(mixed)); // '[1,"hello",true,null]'

Nested Objects​

interface Book {
title: string;
author: string;
year: number;
}

interface Library {
name: string;
books: Book[];
}

let library: Library = {
name: "City Library",
books: [
{ title: "1984", author: "George Orwell", year: 1949 },
{ title: "Brave New World", author: "Aldous Huxley", year: 1932 }
]
};

console.log(JSON.stringify(library, null, 2));

JSON.parse() - JSON to Object​

Convert JSON strings back to JavaScript objects:

Basic Usage​

let jsonString = '{"name":"Alice","age":25}';
let person = JSON.parse(jsonString);

console.log(person.name); // "Alice"
console.log(person.age); // 25
console.log(typeof person); // "object"

Type Safety with Type Assertion​

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

let jsonString = '{"name":"Alice","age":25,"email":"alice@email.com"}';
let user = JSON.parse(jsonString) as User;

console.log(user.name.toUpperCase()); // TypeScript knows it's a string
console.log(user.age.toFixed(2)); // TypeScript knows it's a number

Parsing Arrays​

let jsonArray = '[1,2,3,4,5]';
let numbers: number[] = JSON.parse(jsonArray);

console.log(numbers); // [1, 2, 3, 4, 5]
console.log(numbers.length); // 5
console.log(numbers[0]); // 1

Nested Objects​

interface Address {
street: string;
city: string;
}

interface Person {
name: string;
address: Address;
}

let json = '{"name":"Alice","address":{"street":"123 Main St","city":"London"}}';
let person = JSON.parse(json) as Person;

console.log(person.name); // "Alice"
console.log(person.address.city); // "London"

Error Handling​

JSON.parse() throws an error if the string is invalid:

The Problem​

let badJSON = '{"name": "Alice"'; // Missing closing brace
// let data = JSON.parse(badJSON); // 💥 SyntaxError: Unexpected end of JSON input

Safe Parsing Function​

function safeParse<T>(jsonString: string): T | null {
try {
return JSON.parse(jsonString) as T;
} catch (error) {
console.error("Failed to parse JSON:", error);
return null;
}
}

// Usage
let result = safeParse<{ name: string }>('{"name":"Alice"}');
if (result) {
console.log(result.name); // "Alice"
}

let invalid = safeParse('bad json');
console.log(invalid); // null

With Default Value​

function parseOrDefault<T>(jsonString: string, defaultValue: T): T {
try {
return JSON.parse(jsonString) as T;
} catch {
return defaultValue;
}
}

let user = parseOrDefault('invalid', { name: "Guest", age: 0 });
console.log(user); // { name: "Guest", age: 0 }

Type-Safe JSON Utilities​

Generic JSON Parser​

function parseJSON<T>(jsonString: string): T {
return JSON.parse(jsonString) as T;
}

interface Product {
id: number;
name: string;
price: number;
}

let productJSON = '{"id":1,"name":"Laptop","price":999}';
let product = parseJSON<Product>(productJSON);

console.log(product.name); // Type-safe access
console.log(product.price); // TypeScript knows these properties exist

Validation Function​

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

function isValidUser(obj: any): obj is User {
return (
typeof obj === "object" &&
typeof obj.name === "string" &&
typeof obj.age === "number" &&
typeof obj.email === "string"
);
}

function parseUser(jsonString: string): User | null {
try {
const parsed = JSON.parse(jsonString);
if (isValidUser(parsed)) {
return parsed;
}
return null;
} catch {
return null;
}
}

// Test
let validJSON = '{"name":"Alice","age":25,"email":"alice@email.com"}';
let invalidJSON = '{"name":"Bob"}'; // Missing fields

console.log(parseUser(validJSON)); // User object
console.log(parseUser(invalidJSON)); // null

Working with APIs​

JSON is the standard format for web APIs:

Fetching JSON Data​

interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}

async function fetchTodo(id: number): Promise<Todo | null> {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
const data: Todo = await response.json();
return data;
} catch (error) {
console.error("Failed to fetch:", error);
return null;
}
}

// Usage
fetchTodo(1).then(todo => {
if (todo) {
console.log(todo.title);
console.log(todo.completed);
}
});

Sending JSON Data​

interface CreateUser {
name: string;
email: string;
}

async function createUser(user: CreateUser): Promise<void> {
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(user)
});

const result = await response.json();
console.log("User created:", result);
}

// Usage
createUser({ name: "Alice", email: "alice@email.com" });

Local Storage with JSON​

LocalStorage only stores strings, so you need JSON:

Saving Objects​

interface UserPreferences {
theme: "light" | "dark";
fontSize: number;
notifications: boolean;
}

function savePreferences(prefs: UserPreferences): void {
localStorage.setItem("preferences", JSON.stringify(prefs));
}

function loadPreferences(): UserPreferences | null {
const json = localStorage.getItem("preferences");
if (!json) return null;

try {
return JSON.parse(json) as UserPreferences;
} catch {
return null;
}
}

// Usage
savePreferences({ theme: "dark", fontSize: 16, notifications: true });

let prefs = loadPreferences();
if (prefs) {
console.log(prefs.theme); // "dark"
}

Deep Copying with JSON​

Quick way to clone objects (with limitations):

interface Person {
name: string;
age: number;
address: {
city: string;
};
}

let original: Person = {
name: "Alice",
age: 25,
address: { city: "London" }
};

// Deep copy using JSON
let copy = JSON.parse(JSON.stringify(original)) as Person;

copy.address.city = "Paris";

console.log(original.address.city); // "London" (unchanged)
console.log(copy.address.city); // "Paris"

Limitations:

  • Loses functions/methods
  • Loses undefined values
  • Loses Date objects (becomes string)
  • Loses Symbol properties
  • Can't handle circular references

JSON Configuration Files​

JSON is commonly used for configuration:

package.json Example​

{
"name": "my-project",
"version": "1.0.0",
"description": "A TypeScript project",
"main": "index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"express": "^4.18.0"
}
}

tsconfig.json Example​

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}

Reading Config in TypeScript​

import fs from 'fs';

interface Config {
apiUrl: string;
timeout: number;
retries: number;
}

function loadConfig(path: string): Config {
const fileContent = fs.readFileSync(path, 'utf-8');
return JSON.parse(fileContent) as Config;
}

let config = loadConfig('./config.json');
console.log(config.apiUrl);

Concepts​

Quick Reference​

// Object to JSON string
let obj = { name: "Alice", age: 25 };
let json = JSON.stringify(obj);
// '{"name":"Alice","age":25}'

// Pretty print
let pretty = JSON.stringify(obj, null, 2);

// JSON string to object
let parsed = JSON.parse('{"name":"Alice"}');

// With type safety
interface User { name: string; age: number; }
let user = JSON.parse(json) as User;

// Safe parsing
function safeParse<T>(str: string): T | null {
try {
return JSON.parse(str) as T;
} catch {
return null;
}
}

// Arrays
JSON.stringify([1, 2, 3]); // '[1,2,3]'
JSON.parse('[1,2,3]'); // [1, 2, 3]

Examples​

// User management
interface User {
id: number;
username: string;
email: string;
preferences: {
theme: string;
notifications: boolean;
};
}

let user: User = {
id: 1,
username: "alice",
email: "alice@email.com",
preferences: {
theme: "dark",
notifications: true
}
};

// Save to file or API
let userJSON = JSON.stringify(user, null, 2);
console.log(userJSON);

// Load from file or API
let loadedUser = JSON.parse(userJSON) as User;
console.log(loadedUser.preferences.theme);

// Shopping cart
interface CartItem {
productId: string;
name: string;
price: number;
quantity: number;
}

interface Cart {
items: CartItem[];
total: number;
}

function saveCart(cart: Cart): void {
localStorage.setItem("cart", JSON.stringify(cart));
}

function loadCart(): Cart | null {
const json = localStorage.getItem("cart");
return json ? JSON.parse(json) as Cart : null;
}

Challenges​

1. Create and Parse Book JSON​

  • Create a JSON string for a book with title, author, and year.
  • Parse it into a typed object.
  • Access and print the title.
💡 Show Answer
interface Book {
title: string;
author: string;
year: number;
}

// Create JSON string
let bookJSON = '{"title":"1984","author":"George Orwell","year":1949}';

// Parse with type safety
let book = JSON.parse(bookJSON) as Book;

console.log(book.title); // "1984"
console.log(book.author); // "George Orwell"
console.log(book.year); // 1949

// Alternative: Create from object first
let bookObj: Book = {
title: "Brave New World",
author: "Aldous Huxley",
year: 1932
};

let json = JSON.stringify(bookObj);
console.log(json);

let parsed = JSON.parse(json) as Book;
console.log(parsed.title); // "Brave New World"

// Pretty print
console.log(JSON.stringify(bookObj, null, 2));


2. Array to JSON and Back​

  • Convert an array [10, 20, 30] to JSON.
  • Parse it back to a TypeScript array.
  • Access elements with type safety.
💡 Show Answer
// Original array
let numbers: number[] = [10, 20, 30];

// Convert to JSON
let jsonString: string = JSON.stringify(numbers);
console.log(jsonString); // '[10,20,30]'
console.log(typeof jsonString); // 'string'

// Parse back to array
let parsed: number[] = JSON.parse(jsonString);
console.log(parsed); // [10, 20, 30]
console.log(typeof parsed); // 'object'
console.log(Array.isArray(parsed)); // true

// Type-safe access
console.log(parsed[0]); // 10
console.log(parsed[1]); // 20
console.log(parsed[2]); // 30

// Array methods work
console.log(parsed.map(n => n * 2)); // [20, 40, 60]
console.log(parsed.reduce((a, b) => a + b, 0)); // 60

// Complex array
interface Product {
id: number;
name: string;
price: number;
}

let products: Product[] = [
{ id: 1, name: "Laptop", price: 999 },
{ id: 2, name: "Mouse", price: 29 }
];

let productsJSON = JSON.stringify(products, null, 2);
console.log(productsJSON);

let loadedProducts = JSON.parse(productsJSON) as Product[];
loadedProducts.forEach(p => {
console.log(`${p.name}: $${p.price}`);
});

3. Car Object to JSON​

  • Create a Car interface with make and model.
  • Create a car object and convert it to JSON.
  • Pretty print the result.
💡 Show Answer
interface Car {
make: string;
model: string;
year: number;
color: string;
}

let car: Car = {
make: "Toyota",
model: "Corolla",
year: 2020,
color: "Blue"
};

// Basic stringify
let jsonString = JSON.stringify(car);
console.log(jsonString);
// '{"make":"Toyota","model":"Corolla","year":2020,"color":"Blue"}'

// Pretty printed
let prettyJSON = JSON.stringify(car, null, 2);
console.log(prettyJSON);
/*
{
"make": "Toyota",
"model": "Corolla",
"year": 2020,
"color": "Blue"
}
*/

// With 4 spaces
console.log(JSON.stringify(car, null, 4));

// With tabs
console.log(JSON.stringify(car, null, "\t"));

// Array of cars
let cars: Car[] = [
{ make: "Toyota", model: "Corolla", year: 2020, color: "Blue" },
{ make: "Honda", model: "Civic", year: 2021, color: "Red" }
];

console.log(JSON.stringify(cars, null, 2));


4. Parse Student JSON​

  • Parse the JSON string: {"name":"Bob","age":21,"course":"Math"}
  • Create an interface for Student.
  • Access and print the course property.
💡 Show Answer
interface Student {
name: string;
age: number;
course: string;
}

let jsonString = '{"name":"Bob","age":21,"course":"Math"}';

// Parse with type assertion
let student = JSON.parse(jsonString) as Student;

console.log(student.course); // "Math"
console.log(student.name); // "Bob"
console.log(student.age); // 21

// Type-safe function
function parseStudent(json: string): Student {
return JSON.parse(json) as Student;
}

let bob = parseStudent(jsonString);
console.log(`${bob.name} studies ${bob.course}`);
// "Bob studies Math"

// With validation
function parseStudentSafe(json: string): Student | null {
try {
const parsed = JSON.parse(json);
if (
typeof parsed.name === "string" &&
typeof parsed.age === "number" &&
typeof parsed.course === "string"
) {
return parsed as Student;
}
return null;
} catch {
return null;
}
}

let validStudent = parseStudentSafe(jsonString);
if (validStudent) {
console.log(validStudent.course);
}

let invalidStudent = parseStudentSafe('{"name":"Alice"}');
console.log(invalidStudent); // null

5. Create toJSONString Function​

  • Write a generic function that converts any object to JSON.
  • Add proper type annotations.
  • Test with different object types.
💡 Show Answer
function toJSONString<T>(obj: T): string {
return JSON.stringify(obj);
}

// Test with different types
let person = { name: "Alice", age: 25 };
console.log(toJSONString(person));
// '{"name":"Alice","age":25}'

let numbers = [1, 2, 3, 4, 5];
console.log(toJSONString(numbers));
// '[1,2,3,4,5]'

let mixed = { str: "hello", num: 42, bool: true, arr: [1, 2, 3] };
console.log(toJSONString(mixed));

// With pretty printing option
function toJSONStringPretty `<T>`(obj: T, indent: number = 2): string {
return JSON.stringify(obj, null, indent);
}

console.log(toJSONStringPretty(person));
/*
{
"name": "Alice",
"age": 25
}
*/

// With options
interface JSONOptions {
pretty?: boolean;
indent?: number;
}

function toJSON `<T>`(obj: T, options: JSONOptions = {}): string {
const { pretty = false, indent = 2 } = options;
return pretty ? JSON.stringify(obj, null, indent) : JSON.stringify(obj);
}

console.log(toJSON(person)); // Compact
console.log(toJSON(person, { pretty: true })); // Pretty
console.log(toJSON(person, { pretty: true, indent: 4 })); // 4 spaces


6. Pretty Print Nested Object​

  • Create a nested object with address information.
  • Use JSON.stringify with indentation.
  • Compare compact vs pretty output.
💡 Show Answer
interface Address {
street: string;
city: string;
zipCode: string;
country: string;
}

interface User {
id: number;
name: string;
email: string;
address: Address;
preferences: {
theme: string;
notifications: boolean;
};
}

let user: User = {
id: 1,
name: "Alice",
email: "alice@email.com",
address: {
street: "123 Main St",
city: "Wonderland",
zipCode: "12345",
country: "Fantasy Land"
},
preferences: {
theme: "dark",
notifications: true
}
};

// Compact (default)
console.log("Compact:");
console.log(JSON.stringify(user));

// Pretty with 2 spaces
console.log("\nPretty (2 spaces):");
console.log(JSON.stringify(user, null, 2));

// Pretty with 4 spaces
console.log("\nPretty (4 spaces):");
console.log(JSON.stringify(user, null, 4));

// Pretty with tabs
console.log("\nPretty (tabs):");
console.log(JSON.stringify(user, null, "\t"));

// Just the address
console.log("\nJust address:");
console.log(JSON.stringify(user.address, null, 2));

// Helper function
function display<T>(obj: T, title: string): void {
console.log(`\n${title}:`);
console.log(JSON.stringify(obj, null, 2));
}

display(user, "User Object");
display(user.address, "Address Only");
display(user.preferences, "Preferences Only");

7. Stringify Object with Function​

  • Create an object that contains a function.
  • Stringify it and observe what happens to the function.
  • Explain why functions are omitted.
💡 Show Answer
interface PersonWithMethod {
name: string;
age: number;
greet?: () => string; // Optional because it disappears in JSON
}

let person = {
name: "Alice",
age: 25,
greet() {
return `Hello, I'm ${this.name}`;
},
hobby: "reading"
};

console.log("Original object:");
console.log(person);

// Stringify - function is omitted
let json = JSON.stringify(person);
console.log("\nJSON string:");
console.log(json);
// '{"name":"Alice","age":25,"hobby":"reading"}'
// Notice: greet method is gone!

// Parse back
let parsed = JSON.parse(json);
console.log("\nParsed object:");
console.log(parsed);

// Try to call the method
// parsed.greet(); // ❌ TypeError: parsed.greet is not a function

// Why functions are omitted:
// 1. JSON is for DATA, not CODE
// 2. Functions can't be safely transmitted as text
// 3. Security: you don't want to execute arbitrary code from JSON

// What also gets omitted:
let complexObj = {
string: "hello",
number: 42,
boolean: true,
nullValue: null,
undefinedValue: undefined, // Omitted
method() { }, // Omitted
symbol: Symbol("test"), // Omitted
date: new Date() // Converted to ISO string
};

console.log("\nComplex object stringified:");
console.log(JSON.stringify(complexObj, null, 2));
/*
{
"string": "hello",
"number": 42,
"boolean": true,
"nullValue": null,
"date": "2024-01-01T12:00:00.000Z"
}
*/


8. Compare typeof​

  • Create an object and its JSON string representation.
  • Use typeof to show the difference.
  • Demonstrate that JSON is just a string.
💡 Show Answer
interface Person {
name: string;
age: number;
}

// Original object
let person: Person = { name: "Alice", age: 25 };

// Convert to JSON string
let jsonString: string = JSON.stringify(person);

// Compare types
console.log("Original object:");
console.log(person);
console.log(`Type: ${typeof person}`); // "object"
console.log(`Is object: ${typeof person === "object"}`); // true

console.log("\nJSON string:");
console.log(jsonString);
console.log(`Type: ${typeof jsonString}`); // "string"
console.log(`Is string: ${typeof jsonString === "string"}`); // true

// Key differences
console.log("\nKey differences:");
console.log(`Can access person.name: ${person.name}`); // "Alice"
console.log(`Can access jsonString.name: ${jsonString.name}`); // undefined
console.log(`jsonString length: ${jsonString.length}`); // 25 (characters)

// JSON string is literally just text
console.log("\nJSON string as text:");
console.log(jsonString[0]); // '{'
console.log(jsonString[1]); // '"'

// Parsing converts string back to object
let parsed = JSON.parse(jsonString);
console.log("\nParsed back to object:");
console.log(`Type: ${typeof parsed}`); // "object"
console.log(`Can access parsed.name: ${parsed.name}`); // "Alice"

// Full demonstration
function demonstrateTypes<T>(obj: T): void {
const json = JSON.stringify(obj);
const parsed = JSON.parse(json);

console.log(`\nOriginal: ${typeof obj} - ${obj}`);
console.log(`JSON: ${typeof json} - ${json}`);
console.log(`Parsed: ${typeof parsed} - ${JSON.stringify(parsed)}`);
}

demonstrateTypes({ x: 10, y: 20 });
demonstrateTypes([1, 2, 3]);
demonstrateTypes("hello");
demonstrateTypes(42);

9. Safe Parse Function​

  • Write a safeParse function that handles invalid JSON.
  • Use try-catch for error handling.
  • Return null on parse failure.
💡 Show Answer
function safeParse<T>(jsonString: string): T | null {
try {
return JSON.parse(jsonString) as T;
} catch (error) {
console.error("Failed to parse JSON:", error);
return null;
}
}

// Test with valid JSON
let validJSON = '{"name":"Alice","age":25}';
let result1 = safeParse<{ name: string; age: number }>(validJSON);
console.log(result1); // { name: 'Alice', age: 25 }

// Test with invalid JSON
let invalidJSON = '{"name": "Alice"'; // Missing closing brace
let result2 = safeParse(invalidJSON);
console.log(result2); // null

// More test cases
console.log(safeParse('[1, 2, 3]')); // [1, 2, 3]
console.log(safeParse('not json at all')); // null
console.log(safeParse('{"a": }')); // null
console.log(safeParse('')); // null

// Enhanced version with error details
interface ParseResult `<T>` {
success: boolean;
data?: T;
error?: string;
}

function safeParseDetailed `<T>`(jsonString: string): ParseResult `<T>` {
try {
const data = JSON.parse(jsonString) as T;
return { success: true, data };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}

let result = safeParseDetailed('{"name":"Alice"}');
if (result.success) {
console.log("Parsed successfully:", result.data);
} else {
console.log("Parse failed:", result.error);
}

// With default value
function parseOrDefault `<T>`(jsonString: string, defaultValue: T): T {
try {
return JSON.parse(jsonString) as T;
} catch {
return defaultValue;
}
}

let user = parseOrDefault('invalid json', { name: "Guest", age: 0 });
console.log(user); // { name: "Guest", age: 0 }

// With validation
interface User {
name: string;
age: number;
}

function isValidUser(obj: any): obj is User {
return (
typeof obj === "object" &&
obj !== null &&
typeof obj.name === "string" &&
typeof obj.age === "number"
);
}

function parseUserSafe(jsonString: string): User | null {
try {
const parsed = JSON.parse(jsonString);
return isValidUser(parsed) ? parsed : null;
} catch {
return null;
}
}

console.log(parseUserSafe('{"name":"Alice","age":25}')); // Valid user
console.log(parseUserSafe('{"name":"Bob"}')); // null (missing age)


10. Parse Library JSON​

  • Create a JSON string representing a library with multiple books.
  • Parse it with proper type interfaces.
  • Loop through books and display information.
💡 Show Answer
interface Book {
title: string;
author: string;
year: number;
isbn?: string;
}

interface Library {
name: string;
location: string;
books: Book[];
}

// JSON string
let libraryJSON = `{
"name": "City Central Library",
"location": "Downtown",
"books": [
{
"title": "1984",
"author": "George Orwell",
"year": 1949,
"isbn": "978-0451524935"
},
{
"title": "To Kill a Mockingbird",
"author": "Harper Lee",
"year": 1960
},
{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"year": 1925,
"isbn": "978-0743273565"
}
]
}`;

// Parse the library
let library = JSON.parse(libraryJSON) as Library;

// Display library info
console.log(`Library: ${library.name}`);
console.log(`Location: ${library.location}`);
console.log(`Total books: ${library.books.length}\n`);

// Loop through books
console.log("Books in collection:");
library.books.forEach((book, index) => {
console.log(`${index + 1}. "${book.title}" by ${book.author} (${book.year})`);
if (book.isbn) {
console.log(` ISBN: ${book.isbn}`);
}
});

// Filter and search
function findBooksByAuthor(library: Library, author: string): Book[] {
return library.books.filter(book =>
book.author.toLowerCase().includes(author.toLowerCase())
);
}

let orwellBooks = findBooksByAuthor(library, "Orwell");
console.log(`\nBooks by Orwell:`, orwellBooks);

// Add a book
function addBook(library: Library, book: Book): void {
library.books.push(book);
}

addBook(library, {
title: "Brave New World",
author: "Aldous Huxley",
year: 1932
});

console.log(`\nUpdated total books: ${library.books.length}`);

// Save back to JSON
let updatedJSON = JSON.stringify(library, null, 2);
console.log("\nUpdated library JSON:");
console.log(updatedJSON);

// Statistics
function getLibraryStats(library: Library) {
const oldestBook = library.books.reduce((oldest, book) =>
book.year < oldest.year ? book : oldest
);

const newestBook = library.books.reduce((newest, book) =>
book.year > newest.year ? book : newest
);

const authors = new Set(library.books.map(b => b.author));

return {
totalBooks: library.books.length,
uniqueAuthors: authors.size,
oldestBook: `${oldestBook.title} (${oldestBook.year})`,
newestBook: `${newestBook.title} (${newestBook.year})`
};
}

console.log("\nLibrary Statistics:");
console.log(getLibraryStats(library));

Summary​

In this chapter, you learned:

✅ What JSON is and why it's the standard for data exchange

✅ JSON format rules and valid data types

✅ Converting objects to JSON with JSON.stringify()

✅ Parsing JSON strings with JSON.parse()

✅ Pretty printing JSON with indentation

✅ Handling parse errors with try-catch

✅ Type-safe JSON parsing with TypeScript

✅ Validating parsed data before use

✅ Working with APIs that use JSON

✅ Using JSON with localStorage

✅ Deep copying objects with JSON

Key Takeaways:

  • JSON is just a string format for representing data
  • Use JSON.stringify() to convert objects to JSON strings
  • Use JSON.parse() to convert JSON strings back to objects
  • Always handle parse errors - invalid JSON throws exceptions
  • TypeScript can't automatically type parsed JSON - use type assertions
  • Validate parsed data when it comes from external sources
  • Functions and undefined values are omitted in JSON
  • JSON keys must be double-quoted strings

Best Practices:

  • Always use try-catch when parsing JSON from external sources
  • Create type-safe wrapper functions for parsing
  • Validate parsed data structure before using it
  • Use interfaces to define expected JSON structure
  • Pretty print JSON for debugging (indent with spaces)
  • Never trust data from external APIs - always validate
  • Use type guards to verify parsed data matches your interface
  • Document your JSON data structures with TypeScript interfaces