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
nullinstead - 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
| Feature | JavaScript Object | JSON |
|---|---|---|
| Keys | Can be unquoted | Must be quoted strings |
| String quotes | Single or double | Double only |
| Values | Any type including functions | Limited types |
| Trailing commas | Allowed | Not allowed |
| Comments | Allowed | Not 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 level4- 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
undefinedvalues - Loses
Dateobjects (becomes string) - Loses
Symbolproperties - 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, andyear. - 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
Carinterface 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.stringifywith 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
typeofto 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
safeParsefunction 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