Share
Explore

JavaScript Lab Notebook

References:
Learning outcomes:
These exercises provide a comprehensive understanding of arrays, object-oriented programming, and the use of functions in TypeScript with Node.js.

Lab Exercise 1: Basic Array Functions

Objective: Understand basic array functions like push, pop, shift, and unshift.
typescriptCopy code
let fruits: string[] = [];
fruits.push("apple");
fruits.push("banana");
fruits.unshift("grape");
console.log(fruits); // Expected: ['grape', 'apple', 'banana']

Lab Exercise 2: Searching in an Array

Objective: Use the indexOf and includes functions to search in an array.
typescriptCopy code
let numbers: number[] = [10, 20, 30, 40, 50];
console.log(numbers.indexOf(30)); // Expected: 2
console.log(numbers.includes(25)); // Expected: false

Lab Exercise 3: Sorting an Array

Objective: Sort an array using the sort function.
typescriptCopy code
let names: string[] = ["Zoe", "Anna", "Charlie"];
names.sort();
console.log(names); // Expected: ['Anna', 'Charlie', 'Zoe']

Lab Exercise 4: Joining Arrays

Objective: Use the join function to convert an array to a string.
typescriptCopy code
let colors: string[] = ["red", "green", "blue"];
console.log(colors.join(", ")); // Expected: 'red, green, blue'

Lab Exercise 5: Creating a Class

Objective: Create a simple class and instantiate an object.
typescriptCopy code
class Car {
constructor(public brand: string, public model: string) {}
}

let myCar = new Car("Toyota", "Camry");
console.log(myCar.brand); // Expected: 'Toyota'

Lab Exercise 6: Class Inheritance

Objective: Understand inheritance by extending a class.
typescriptCopy code
class Vehicle {
constructor(public type: string) {}
}

class Bike extends Vehicle {
constructor(public brand: string) {
super("Bike");
}
}

let myBike = new Bike("Yamaha");
console.log(myBike.type); // Expected: 'Bike'

image.png

Lab Exercise 7: Polymorphism

Objective: Use polymorphism to override methods.
typescriptCopy code
class Animal {
speak(): string {
return "Some sound";
}
}

class Dog extends Animal {
speak(): string {
return "Woof!";
}
}

let dog = new Dog();
console.log(dog.speak()); // Expected: 'Woof!'

Lab Exercise 8: Storing Objects in an Array

Objective: Create multiple objects and store them in an array.
typescriptCopy code
class Student {
constructor(public name: string, public age: number) {}
}

let students: Student[] = [
new Student("Alice", 20),
new Student("Bob", 22)
];
console.log(students[0].name); // Expected: 'Alice'

Lab Exercise 9: Iterating Over Objects in an Array

Objective: Use the forEach function to iterate over objects in an array.
typescriptCopy code
students.forEach(student => {
console.log(`${student.name} is ${student.age} years old.`);
});

Lab Exercise 10: Using Map with Objects

Objective: Convert an array of objects to another array using the map function.
typescriptCopy code
let ages: number[] = students.map(student => student.age);
console.log(ages); // Expected: [20, 22]


Lab Exercises: Series 2:


Apply exceptions, error handling, Iterators and Generators.
4.1 Incorporate exception handling with try and catch
4.2 Work with the Error object and throw errors
4.3 Understand try-catch-finally block of statements
4.4 Use the iteration protocol and generators

Lab Exercise 1: Basic Exception Handling

Objective: Understand how to use try and catch to handle exceptions.
typescriptCopy code
try {
let result = 10 / 0; // This won't actually throw an error, but for demonstration purposes.
} catch (error) {
console.error("An error occurred:", error.message);
}

Lab Exercise 2: Throwing Errors

Objective: Learn how to throw custom errors using the Error object.
typescriptCopy code
function divide(a: number, b: number): number {
if (b === 0) {
throw new Error("Division by zero is not allowed.");
}
return a / b;
}

try {
let result = divide(10, 0);
} catch (error) {
console.error(error.message); // Expected: 'Division by zero is not allowed.'
}

Lab Exercise 3: Using Finally Block

Objective: Understand the finally block and its use cases.
typescriptCopy code
try {
let result = 10 / 5;
console.log(result);
} catch (error) {
console.error("An error occurred:", error.message);
} finally {
console.log("This will always run, regardless of an error.");
}

Lab Exercise 4: Iteration Protocol

Objective: Use the iteration protocol to create a custom iterator.
typescriptCopy code
class Range {
constructor(public start: number, public end: number) {}

[Symbol.iterator]() {
let current = this.start;
let end = this.end;

return {
next() {
if (current <= end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
}

let range = new Range(1, 5);
for (let num of range) {
console.log(num); // Expected: 1, 2, 3, 4, 5
}

Lab Exercise 5: Basic Generator Function

Objective: Understand how to create and use a generator function.
typescriptCopy code
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}

for (let num of numberGenerator()) {
console.log(num); // Expected: 1, 2, 3
}

Lab Exercise 6: Generator with Parameters

Objective: Use parameters with generator functions.
typescriptCopy code
function* rangeGenerator(start: number, end: number) {
while (start <= end) {
yield start++;
}
}

for (let num of rangeGenerator(1, 3)) {
console.log(num); // Expected: 1, 2, 3
}

Lab Exercise 7: Error Handling with Generators

Objective: Handle errors within generator functions.
typescriptCopy code
function* errorGenerator() {
try {
yield "Start";
throw new Error("Error in generator");
yield "End"; // This won't be executed
} catch (error) {
yield error.message;
}
}

for (let value of errorGenerator()) {
console.log(value); // Expected: 'Start', 'Error in generator'
}

Lab Exercise 8: Using Return with Generators

Objective: Understand the use of the return statement in generators.
typescriptCopy code
function* returnGenerator() {
yield "Start";
return "Returned Value";
yield "End"; // This won't be executed
}

for (let value of returnGenerator()) {
console.log(value); // Expected: 'Start'
}

Lab Exercise 9: Combining Generators

Objective: Combine multiple generators using the yield* expression.
typescriptCopy code
function* generatorA() {
yield 1;
yield 2;
}

function* generatorB() {
yield* generatorA();
yield 3;
}

for (let value of generatorB()) {
console.log(value); // Expected: 1, 2, 3
}

Lab Exercise 10: Async Generators

Objective: Understand how to create and use async generator functions.
typescriptCopy code
async function* asyncNumberGenerator() {
await new Promise(resolve => setTimeout(resolve, 1000));
yield 1;
await new Promise(resolve => setTimeout(resolve, 1000));
yield 2;
}

(async () => {
for await (let num of asyncNumberGenerator()) {
console.log(num); // Expected: 1 (after 1s), 2 (after another 1s)
}
})();

These exercises should provide a comprehensive understanding of exceptions, error handling, iterators, and generators in TypeScript with Node.js.

ok

More Errors:


Here are three more use cases for handling errors in TypeScript with Node.js:

Use Case 1: Validating User Input

When dealing with user input, it's crucial to validate the data before processing it. This ensures that the application behaves correctly and securely.
typescriptCopy code
function processUserData(age: number, email: string) {
if (typeof age !== 'number' || age <= 0) {
throw new Error("Invalid age provided.");
}

if (!email.includes("@")) {
throw new Error("Invalid email address.");
}

// Continue processing the user data...
}

try {
processUserData(25, "example.com"); // Invalid email
} catch (error) {
console.error("Error:", error.message);
}

Use Case 2: Handling File Operations

File operations can fail for various reasons, such as the file not existing or lacking permissions. Proper error handling ensures that the application can gracefully handle these situations.
typescriptCopy code
import { promises as fs } from 'fs';

async function readFileContent(filePath: string): Promise<string> {
try {
const content = await fs.readFile(filePath, 'utf-8');
return content;
} catch (error) {
throw new Error(`Failed to read file at ${filePath}. Reason: ${error.message}`);
}
}

(async () => {
try {
const content = await readFileContent("nonexistent.txt");
console.log(content);
} catch (error) {
console.error("Error:", error.message);
}
})();

Use Case 3: Handling Network Requests

Network requests can fail due to various reasons, such as server errors, timeouts, or connectivity issues. Proper error handling ensures that the application can provide feedback to the user or retry the request if necessary.
typescriptCopy code
import axios from 'axios';

async function fetchData(url: string): Promise<any> {
try {
const response = await axios.get(url);
return response.data;
} catch (error) {
if (error.response) {
// The request was made, but the server responded with a status code outside of the 2xx range
throw new Error(`Server responded with status code ${error.response.status}.`);
} else if (error.request) {
// The request was made, but no response was received
throw new Error("No response received from the server.");
} else {
// Something else happened while setting up the request
throw new Error(`Failed to fetch data. Reason: ${error.message}`);
}
}
}

(async () => {
try {
const data = await fetchData("https://api.example.com/data");
console.log(data);
} catch (error) {
console.error("Error:", error.message);
}
})();

These use cases demonstrate the importance of error handling in various scenarios, ensuring that the application remains robust and user-friendly.
image.jpeg failed to upload
are errors ever used in situations NOT involving try / catch ?
ChatGPT
Yes, errors can be used in situations that don't involve the try/catch mechanism. Here are some scenarios where errors might be used without directly employing try/catch:
Promises and Async/Await: When working with promises, errors can be thrown and then handled using the .catch() method of the promise. With async/await, if you don't wrap the await in a try/catch, the error will propagate up and can be caught by a higher-level catch() or result in an unhandled promise rejection.
typescriptCopy code
someAsyncFunction() .then(result => { // process result }) .catch(error => { console.error("Error:", error.message); });
Event Emitters: In Node.js, event-driven architectures might emit an 'error' event when something goes wrong. Listeners can then be set up to handle these errors.
javascriptCopy code
const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('error', (error) => { console.error("An error occurred:", error.message); }); myEmitter.emit('error', new Error('Oops!'));
Middleware (like in Express.js): In Express.js, a popular web framework for Node.js, errors can be passed to the next middleware in line, which might be a specialized error-handling middleware.
javascriptCopy code
const express = require('express'); const app = express(); app.get('/', (req, res, next) => { if (someCondition) { next(new Error('Something went wrong!')); } else { res.send('Hello World'); } }); app.use((error, req, res, next) => { res.status(500).send(`Error: ${error.message}`); });
Custom Error Handling: In some scenarios, rather than throwing an error, you might want to return an error object or a result object that encapsulates both the result and any potential error. This pattern is sometimes seen in Go, but it can be used in JavaScript as well.
typescriptCopy code
function someFunction(): { error?: Error, data?: any } { if (someErrorCondition) { return { error: new Error('An error occurred') }; } return { data: 'Some data' }; } const result = someFunction(); if (result.error) { console.error(result.error.message); } else { console.log(result.data); }
Assertions in Testing: In testing frameworks, assertions that fail often throw errors. These errors might not be caught with try/catch directly in the test, but the testing framework itself will catch and report them.
Type Errors and Runtime Checks: Sometimes, you might use errors to enforce certain conditions or invariants in your code, even if you don't catch them immediately.
typescriptCopy code
function requireString(value: any): string { if (typeof value !== 'string') { throw new TypeError('Expected a string'); } return value; }
In all these scenarios, even if there's no immediate try/catch, there's usually some mechanism in place to handle or report the error, whether it's a .catch() method, an error event listener, middleware, or something else.

Lab Exercises Series 3: Utilize Asynchronous programming, DOM and debugging your code.


5.1 Use callback functions properly
5.2 Create and use promises in your code
5.3 Query various Document Object Model (DOM) elements
5.4 Manipulate existing elements and create new DOM elements
5.5 Utilize the JavaScript console for code debugging

Lab Exercise 1: Callback Functions

Objective: Understand how to use callback functions to handle asynchronous operations.
typescriptCopy code
function fetchData(callback: (data: string) => void) {
setTimeout(() => {
callback("Data fetched after 2 seconds");
}, 2000);
}

fetchData((data) => {
console.log(data); // Expected: 'Data fetched after 2 seconds'
});

Lab Exercise 2: Creating Promises

Objective: Create a promise that resolves after a certain duration.
typescriptCopy code
function delay(duration: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
}

delay(2000).then(() => {
console.log("2 seconds have passed!");
});

Lab Exercise 3: Using Promises

Objective: Fetch data using promises.
typescriptCopy code
function fetchData(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Data fetched using promises");
}, 2000);
});
}

fetchData().then(data => {
console.log(data); // Expected: 'Data fetched using promises'
});

Lab Exercise 4: Querying DOM Elements

Objective: Use TypeScript to query a DOM element.
(Note: This exercise assumes a browser environment.)
typescriptCopy code
// Assuming there's an element <div id="myDiv">Hello</div> in the HTML
const myDiv: HTMLElement | null = document.getElementById("myDiv");
if (myDiv) {
console.log(myDiv.textContent); // Expected: 'Hello'
}

Lab Exercise 5: Manipulating DOM Elements

Objective: Change the content of a DOM element.
(Note: This exercise assumes a browser environment.)
typescriptCopy code
const myDiv: HTMLElement | null = document.getElementById("myDiv");
if (myDiv) {
myDiv.textContent = "Hello, World!";
}

Lab Exercise 6: Creating New DOM Elements

Objective: Create a new DOM element and append it to the body.
(Note: This exercise assumes a browser environment.)
typescriptCopy code
const newDiv: HTMLElement = document.createElement("div");
newDiv.textContent = "This is a new div!";
document.body.appendChild(newDiv);

Lab Exercise 7: Debugging Using console.log

Objective: Use console.log to debug and view the content of an array.
typescriptCopy code
const numbers: number[] = [1, 2, 3, 4, 5];
console.log(numbers); // Use the browser's or Node.js console to view the output

Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.