Table of Contents
Open Table of Contents
- Introduction
- Variables
- If statements
- While loop
- For loop
- Division and Math Operations
- Arrays
- Sorting and reversing an array
- Array methods and functional programming
- Strings
- Queues and Deques
- Set (HashSet)
- Map (HashMap)
- Tuples
- Heaps (Priority Queue)
- Functions
- Nested functions and closures
- Classes
- Resources
Introduction
Recently, I’ve been brushing up my coding interview’s skills by studying data structures and algorithms. This isn’t the first time (of course) I’ve done this, but this time I decided to use TypeScript as my main programming language, given its strong type system, JavaScript compatibility, and excellent IDE support that makes it perfect for coding interviews where type safety can prevent bugs.
Variables
// Defining a variable with explicit types
let language: string = "TypeScript";
// Set it to null or undefined
language = null;
// or
language = undefined;
// Defining multiple variables at the same time
let slow: number = 0, fast: number = 10;
// Swapping variables (using destructuring assignment)
[slow, fast] = [fast, slow];
// Type inference (TypeScript can infer types)
let count = 5; // TypeScript infers this as number
let name = "Alice"; // TypeScript infers this as string
If statements
In TypeScript, we use curly braces {} to determine the scope of conditionals and use JavaScript-style boolean operators.
let age: number = 25;
if (age < 18) {
console.log("You are a boy 👦🏽");
} else if (age >= 18 && age <= 65) {
console.log("You are an adult 👨🏽");
} else {
console.log("You are old 👴🏽");
}
For multi-line comparisons, we can use parentheses for clarity:
let n: number = 1, m: number = 2;
if ((n > 2 &&
n !== m) || n === m) {
n += 1;
}
TypeScript uses
&&,||for boolean operators and!for negation, just like JavaScript. We also have===for strict equality and!==for strict inequality.
While loop
let n: number = 10;
while (n > 0) {
console.log(n);
n--; // or n -= 1
}
For loop
// Traditional for loop from i = 0 to i = 9
for (let i = 0; i < 10; i++) {
console.log(i); // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
}
// Looping from i = 2 to i = 5
for (let i = 2; i < 6; i++) {
console.log(i); // 2, 3, 4, 5
}
// Looping in reverse from i = 5 to i = 2
for (let i = 5; i > 1; i--) {
console.log(i); // 5, 4, 3, 2
}
Creating a range function in TypeScript
TypeScript doesn’t have a built-in range function like Python, but we can create one:
// Simple range function
function range(start: number, stop?: number, step: number = 1): number[] {
if (stop === undefined) {
stop = start;
start = 0;
}
const result: number[] = [];
if (step > 0) {
for (let i = start; i < stop; i += step) {
result.push(i);
}
} else {
for (let i = start; i > stop; i += step) {
result.push(i);
}
}
return result;
}
// Usage examples
range(10); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(2, 6); // [2, 3, 4, 5]
range(5, 1, -1); // [5, 4, 3, 2]
Division and Math Operations
// Division is decimal by default
console.log(5 / 2); // 2.5
// For integer division, use Math.floor() for positive numbers
console.log(Math.floor(5 / 2)); // 2
// ⚠️ CAREFUL: Math.floor() always rounds down (towards negative infinity)
// For negative numbers this behaves differently than most languages
console.log(Math.floor(-3 / 2)); // -2
// To round towards zero (truncate), use Math.trunc()
console.log(Math.trunc(-3 / 2)); // -1
// Modulo operator behaves like JavaScript
console.log(10 % 3); // 1
console.log(-10 % 3); // -1 (different from Python!)
Math functions and constants
// Common math functions
console.log(Math.floor(3 / 2)); // 1
console.log(Math.ceil(3 / 2)); // 2
console.log(Math.sqrt(2)); // 1.414...
console.log(Math.pow(2, 3)); // 8
console.log(2 ** 3); // 8 (exponentiation operator)
// Min and max values
console.log(Number.POSITIVE_INFINITY); // Infinity
console.log(Number.NEGATIVE_INFINITY); // -Infinity
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
Arrays
// Defining arrays with type annotations
let arr: number[] = [1, 2, 3, 4, 5, 6];
// Alternative syntax
let arr2: Array<number> = [1, 2, 3, 4, 5, 6];
// Indexing a value from an array
let n: number = arr[1]; // 2
// Getting the last element (no negative indexing like Python)
let last: number = arr[arr.length - 1]; // 6
// Getting the second to last element
let secondLast: number = arr[arr.length - 2]; // 5
// Get the length of the array
console.log(arr.length); // 6
// Adding a value to the end
arr.push(7); // [1, 2, 3, 4, 5, 6, 7]
// Inserting a value at a specific index (O(N) operation)
arr.splice(2, 0, 8); // [1, 2, 8, 3, 4, 5, 6, 7]
// Removing the last element
n = arr.pop()!; // 7 (! tells TypeScript pop() won't return undefined)
// Array slicing (slice method doesn't mutate original array)
let newArr: number[] = arr.slice(1, 3); // [2, 8]
// Destructuring assignment (unpacking)
let [a, b] = newArr; // a = 2, b = 8
let [first, ...rest] = arr; // first = 1, rest = [2, 8, 3, 4, 5, 6]
// Initializing an array of size n with default value
let n_size: number = 5;
let defaultArr: number[] = new Array(n_size).fill(1); // [1, 1, 1, 1, 1]
// Alternative using Array.from
let defaultArr2: number[] = Array.from({length: n_size}, () => 1);
// Traversing an array without index (for...of loop)
arr = [1, 2, 3, 4, 5];
for (const num of arr) {
console.log(num); // 1, 2, 3, 4, 5
}
// Traversing an array with index (traditional for loop)
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]); // 1, 2, 3, 4, 5
}
// Traversing with both index and value using forEach
arr.forEach((num, index) => {
console.log(index, num); // 0 1, 1 2, 2 3, 3 4, 4 5
});
// Traversing with both index and value using entries()
for (const [index, num] of arr.entries()) {
console.log(index, num); // 0 1, 1 2, 2 3, 3 4, 4 5
}
// Traverse multiple arrays simultaneously using zip-like functionality
const nums1: number[] = [1, 3, 5];
const nums2: number[] = [2, 4, 6];
for (let i = 0; i < Math.min(nums1.length, nums2.length); i++) {
console.log(nums1[i], nums2[i]); // 1 2, 3 4, 5 6
}
// Or using a custom zip function
function zip<T, U>(arr1: T[], arr2: U[]): [T, U][] {
return arr1.map((item, index) => [item, arr2[index]]);
}
for (const [n1, n2] of zip(nums1, nums2)) {
console.log(n1, n2); // 1 2, 3 4, 5 6
}
Sorting and reversing an array
// Reverse (mutates original array)
let arr: number[] = [1, 2, 3, 4];
arr.reverse(); // [4, 3, 2, 1]
// Non-mutating reverse
let reversed: number[] = [...arr].reverse();
// Sorting in ascending order (mutates original array)
arr = [4, 3, -2, -1];
arr.sort((a, b) => a - b); // [-2, -1, 3, 4]
// Sorting in descending order
arr = [4, 3, -2, -1];
arr.sort((a, b) => b - a); // [4, 3, -1, -2]
// Non-mutating sort
let sorted: number[] = [...arr].sort((a, b) => a - b);
// Sorting strings by alphabetical order
let names: string[] = ["Bob", "Alice", "Zoe", "Kevin"];
names.sort(); // ["Alice", "Bob", "Kevin", "Zoe"]
// Sorting strings by length
names.sort((a, b) => a.length - b.length); // ["Bob", "Zoe", "Alice", "Kevin"]
// Sorting objects by property
interface Person {
name: string;
age: number;
}
let people: Person[] = [
{name: "Alice", age: 30},
{name: "Bob", age: 25},
{name: "Charlie", age: 35}
];
// Sort by age
people.sort((a, b) => a.age - b.age);
Array methods and functional programming
// Initialize an array from 0 to 4 (equivalent to list comprehension)
let arr: number[] = Array.from({length: 5}, (_, i) => i); // [0, 1, 2, 3, 4]
// Alternative using spread and keys
let arr2: number[] = [...Array(5).keys()]; // [0, 1, 2, 3, 4]
// Transform array (equivalent to [i+i for i in range(5)])
let doubled: number[] = Array.from({length: 5}, (_, i) => i + i); // [0, 2, 4, 6, 8]
// Using map
let doubled2: number[] = arr.map(i => i + i); // [0, 2, 4, 6, 8]
// 2D array (3x3 matrix with 0 values)
let matrix: number[][] = Array.from({length: 3}, () =>
Array.from({length: 3}, () => 0)
); // [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
// Alternative 2D array initialization
let matrix2: number[][] = Array(3).fill(null).map(() => Array(3).fill(0));
// Filtering arrays (equivalent to list comprehension with condition)
let numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let evens: number[] = numbers.filter(n => n % 2 === 0); // [2, 4, 6, 8, 10]
// Map and filter combined
let evenSquares: number[] = numbers
.filter(n => n % 2 === 0)
.map(n => n * n); // [4, 16, 36, 64, 100]
Strings
// String slicing using substring or slice
let s: string = "Hello TypeScript!";
console.log(s.substring(0, 5)); // "Hello"
console.log(s.slice(0, 5)); // "Hello"
// Strings are immutable in TypeScript too
// s[0] = "h"; // Error: Index signature in type 'string' only permits reading
// String concatenation creates a new string (O(N) operation)
s += ", nice to meet you.";
console.log(s); // "Hello TypeScript!, nice to meet you."
// Converting strings to numbers
console.log(parseInt("123") + parseInt("10")); // 133
console.log(Number("123") + Number("10")); // 133
console.log(+"123" + +"10"); // 133 (unary plus operator)
// Converting numbers to strings
console.log(String(123) + String(10)); // "12310"
console.log(123 + "" + 10); // "12310"
console.log(`${123}${10}`); // "12310" (template literals)
// Getting the ASCII/Unicode value from a character
console.log("A".charCodeAt(0)); // 65
// Getting character from ASCII/Unicode value
console.log(String.fromCharCode(65)); // "A"
// Joining arrays of strings
let words: string[] = ["Hello", "TypeScript", "!"];
console.log(words.join("")); // "HelloTypeScript!"
console.log(words.join(" ")); // "Hello TypeScript !"
// Splitting strings
let sentence: string = "Hello world from TypeScript";
let wordsArray: string[] = sentence.split(" "); // ["Hello", "world", "from", "TypeScript"]
// Common string methods
console.log(s.toLowerCase()); // convert to lowercase
console.log(s.toUpperCase()); // convert to uppercase
console.log(s.includes("Type")); // check if string contains substring
console.log(s.startsWith("Hello")); // check if string starts with substring
console.log(s.endsWith("you.")); // check if string ends with substring
Queues and Deques
// TypeScript doesn't have a built-in deque, but we can use arrays
// Note: shift() and unshift() operations are O(n), not O(1)
let queue: number[] = [];
// Adding values to the end of the queue (O(1))
queue.push(1);
queue.push(2);
console.log(queue); // [1, 2]
// Remove from the beginning (O(n) - not efficient for large arrays)
let item: number | undefined = queue.shift();
console.log(queue); // [2]
console.log(item); // 1
// Add to the beginning (O(n) - not efficient for large arrays)
queue.unshift(1);
console.log(queue); // [1, 2]
// Remove from the end (O(1))
item = queue.pop();
console.log(queue); // [1]
console.log(item); // 2
// For better performance with frequent front operations,
// implement a custom deque or use a library
class Deque<T> {
private items: T[] = [];
private head: number = 0;
private tail: number = 0;
addBack(item: T): void {
this.items[this.tail++] = item;
}
addFront(item: T): void {
this.items[--this.head] = item;
}
removeBack(): T | undefined {
if (this.tail <= this.head) return undefined;
const item = this.items[--this.tail];
delete this.items[this.tail];
return item;
}
removeFront(): T | undefined {
if (this.tail <= this.head) return undefined;
const item = this.items[this.head];
delete this.items[this.head++];
return item;
}
size(): number {
return this.tail - this.head;
}
}
// Usage
let deque = new Deque<number>();
deque.addBack(1);
deque.addBack(2);
deque.addFront(0);
console.log(deque.removeFront()); // 0
Set (HashSet)
Sometimes, in coding interview questions we need to keep a unique list of values and a Set is the perfect data structure to achieve it.
// Defining a set
let mySet: Set<number> = new Set();
// Adding values
mySet.add(1);
mySet.add(2);
console.log(mySet); // Set(2) {1, 2}
// Getting the size of the set
console.log(mySet.size); // 2
// Checking if a value exists in the set
console.log(mySet.has(1)); // true
console.log(mySet.has(2)); // true
console.log(mySet.has(3)); // false
// Removing a value from a set
mySet.delete(2);
console.log(mySet.has(2)); // false
// Array to set (removes duplicates)
let numbers: Set<number> = new Set([1, 2, 3, 4, 5, 1, 2]);
console.log(numbers); // Set(5) {1, 2, 3, 4, 5}
// Set from array using spread operator
let uniqueNumbers: number[] = [...new Set([1, 2, 2, 3, 3, 4])];
console.log(uniqueNumbers); // [1, 2, 3, 4]
// Creating set with initial values
let mySet2: Set<number> = new Set(Array.from({length: 5}, (_, i) => i));
console.log(mySet2); // Set(5) {0, 1, 2, 3, 4}
// Set operations
let set1: Set<number> = new Set([1, 2, 3]);
let set2: Set<number> = new Set([3, 4, 5]);
// Union
let union: Set<number> = new Set([...set1, ...set2]); // {1, 2, 3, 4, 5}
// Intersection
let intersection: Set<number> = new Set([...set1].filter(x => set2.has(x))); // {3}
// Difference
let difference: Set<number> = new Set([...set1].filter(x => !set2.has(x))); // {1, 2}
Map (HashMap)
One the most (if not the most) common data structures while doing coding interviews is a Map, this data structure allows us to keep a list of values in a key: value pair format.
// Defining a map
let myMap: Map<string, number> = new Map();
myMap.set("Alice", 23);
myMap.set("Bob", 34);
console.log(myMap); // Map(2) {"Alice" => 23, "Bob" => 34}
// Getting the size of the map
console.log(myMap.size); // 2
// Updating a key in the map
myMap.set("Alice", 45);
console.log(myMap); // Map(2) {"Alice" => 45, "Bob" => 34}
// Checking if a key exists in the map
console.log(myMap.has("Alice")); // true
console.log(myMap.has("Gabriel")); // false
// Getting a value
console.log(myMap.get("Alice")); // 45
// Deleting a key from a map
let hadKey: boolean = myMap.delete("Alice");
console.log(hadKey); // true
console.log(myMap); // Map(1) {"Bob" => 34}
// Initializing a map with values
let myMap2: Map<string, number> = new Map([
["Carlos", 10],
["Gustavo", 30],
["Juan", 97]
]);
// Map from array (equivalent to dict comprehension)
let numberMap: Map<number, number> = new Map(
Array.from({length: 4}, (_, i) => [i, i * 2])
); // Map(4) {0 => 0, 1 => 2, 2 => 4, 3 => 6}
// Looping through a map - keys only
for (const key of myMap2.keys()) {
console.log(key, myMap2.get(key)); // Carlos 10, Gustavo 30, Juan 97
}
// Looping through a map - key-value pairs
for (const [key, value] of myMap2.entries()) {
console.log(key, value); // Carlos 10, Gustavo 30, Juan 97
}
// Looping only the values
for (const value of myMap2.values()) {
console.log(value); // 10, 30, 97
}
// Alternative: Using plain objects (when keys are strings)
let objMap: {[key: string]: number} = {
"Carlos": 10,
"Gustavo": 30,
"Juan": 97
};
// Object methods
console.log("Carlos" in objMap); // true
console.log(Object.keys(objMap)); // ["Carlos", "Gustavo", "Juan"]
console.log(Object.values(objMap)); // [10, 30, 97]
console.log(Object.entries(objMap)); // [["Carlos", 10], ["Gustavo", 30], ["Juan", 97]]
In TypeScript we have both
Mapobjects and plain objects{}. Maps are better for dynamic keys and have better performance for frequent additions/deletions, while objects are better for records with known string keys.
Tuples
Tuples in TypeScript are arrays with a fixed length and known types at each position.
// Defining a tuple with type annotation
let tup: [number, number, number] = [1, 2, 3];
console.log(tup); // [1, 2, 3]
// Getting a value (with type safety)
console.log(tup[0]); // 1 (TypeScript knows this is a number)
// Tuples are mutable in TypeScript (unlike Python)
tup[0] = 5; // This works!
console.log(tup); // [5, 2, 3]
// For immutability, use readonly tuples
let readonlyTup: readonly [number, number, number] = [1, 2, 3];
// readonlyTup[0] = 5; // Error: Cannot assign to '0' because it is a read-only property
// Tuples with different types
let mixedTup: [string, number, boolean] = ["Alice", 25, true];
let [name, age, isActive] = mixedTup; // Destructuring with type inference
// Using tuples as Map keys (convert to string or use JSON.stringify)
let coordMap: Map<string, number> = new Map();
coordMap.set(JSON.stringify([1, 2]), 3);
coordMap.set(JSON.stringify([3, 4]), 7);
// Helper function for tuple keys
function tupleKey(tuple: readonly number[]): string {
return JSON.stringify(tuple);
}
coordMap.set(tupleKey([5, 6]), 11);
console.log(coordMap.get(tupleKey([1, 2]))); // 3
// Using tuples in Sets
let coordSet: Set<string> = new Set();
coordSet.add(tupleKey([1, 2]));
console.log(coordSet.has(tupleKey([1, 2]))); // true
// Optional tuple elements
let optionalTup: [number, number, number?] = [1, 2]; // Third element is optional
// Rest elements in tuples
let restTup: [number, ...string[]] = [1, "a", "b", "c"];
let [first, ...strings] = restTup; // first: number, strings: string[]
Heaps (Priority Queue)
Heaps are another really common data structure to find the min and max values from a set of elements. TypeScript doesn’t have a built-in heap, but we can implement one or use a library.
// Simple MinHeap implementation
class MinHeap {
private heap: number[] = [];
private getParentIndex(index: number): number {
return Math.floor((index - 1) / 2);
}
private getLeftChildIndex(index: number): number {
return 2 * index + 1;
}
private getRightChildIndex(index: number): number {
return 2 * index + 2;
}
private swap(index1: number, index2: number): void {
[this.heap[index1], this.heap[index2]] = [this.heap[index2], this.heap[index1]];
}
insert(value: number): void {
this.heap.push(value);
this.heapifyUp(this.heap.length - 1);
}
private heapifyUp(index: number): void {
const parentIndex = this.getParentIndex(index);
if (parentIndex >= 0 && this.heap[parentIndex] > this.heap[index]) {
this.swap(parentIndex, index);
this.heapifyUp(parentIndex);
}
}
extractMin(): number | undefined {
if (this.heap.length === 0) return undefined;
if (this.heap.length === 1) return this.heap.pop();
const min = this.heap[0];
this.heap[0] = this.heap.pop()!;
this.heapifyDown(0);
return min;
}
private heapifyDown(index: number): void {
const leftChildIndex = this.getLeftChildIndex(index);
const rightChildIndex = this.getRightChildIndex(index);
let smallestIndex = index;
if (leftChildIndex < this.heap.length &&
this.heap[leftChildIndex] < this.heap[smallestIndex]) {
smallestIndex = leftChildIndex;
}
if (rightChildIndex < this.heap.length &&
this.heap[rightChildIndex] < this.heap[smallestIndex]) {
smallestIndex = rightChildIndex;
}
if (smallestIndex !== index) {
this.swap(index, smallestIndex);
this.heapifyDown(smallestIndex);
}
}
peek(): number | undefined {
return this.heap.length > 0 ? this.heap[0] : undefined;
}
size(): number {
return this.heap.length;
}
}
// Usage
let minHeap = new MinHeap();
minHeap.insert(3);
minHeap.insert(2);
minHeap.insert(4);
console.log(minHeap.peek()); // 2
while (minHeap.size() > 0) {
console.log(minHeap.extractMin()); // 2, 3, 4
}
// MaxHeap (negate values or modify comparison)
class MaxHeap extends MinHeap {
insert(value: number): void {
super.insert(-value); // Negate for max heap behavior
}
extractMax(): number | undefined {
const result = super.extractMin();
return result !== undefined ? -result : undefined;
}
peekMax(): number | undefined {
const result = super.peek();
return result !== undefined ? -result : undefined;
}
}
let maxHeap = new MaxHeap();
maxHeap.insert(3);
maxHeap.insert(2);
maxHeap.insert(4);
console.log(maxHeap.peekMax()); // 4
while (maxHeap.size() > 0) {
console.log(maxHeap.extractMax()); // 4, 3, 2
}
Functions
// Function declaration with type annotations
function add(a: number, b: number): number {
return a + b;
}
console.log(add(10, 20)); // 30
// Arrow function
const multiply = (a: number, b: number): number => {
return a * b;
};
// Arrow function with implicit return
const subtract = (a: number, b: number): number => a - b;
// Function with optional parameters
function greet(name: string, greeting?: string): string {
return `${greeting || "Hello"}, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet("Bob", "Hi")); // "Hi, Bob!"
// Function with default parameters
function power(base: number, exponent: number = 2): number {
return Math.pow(base, exponent);
}
console.log(power(5)); // 25
console.log(power(5, 3)); // 125
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// Function types
type MathOperation = (a: number, b: number) => number;
const divide: MathOperation = (a, b) => a / b;
// Generic functions
function identity<T>(arg: T): T {
return arg;
}
console.log(identity<string>("hello")); // "hello"
console.log(identity<number>(42)); // 42
Nested functions and closures
This kind of functions are useful when dealing with graphs and algorithms that use recursion.
// Defining nested functions
function outer(a: string, b: string): string {
const c: string = "c";
function inner(): string {
return a + b + c; // Can access outer function's parameters and variables
}
return inner();
}
console.log(outer("a", "b")); // "abc"
// Closures - inner function has access to outer scope
function createCounter(): () => number {
let count = 0;
return function(): number {
return ++count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// Modifying arrays and objects from nested functions
function processData(arr: number[], multiplier: number): void {
function helper(): void {
// Arrays and objects can be modified directly (they're passed by reference)
for (let i = 0; i < arr.length; i++) {
arr[i] *= 2;
}
// For primitive values, we need to modify through the outer scope
// We can't reassign parameters, but we can modify properties of objects
}
helper();
// If we need to modify primitive values, we can return them or use objects
const result = { originalMultiplier: multiplier, newMultiplier: multiplier * 2 };
console.log(arr, result);
}
const nums: number[] = [1, 2, 3];
processData(nums, 5); // [2, 4, 6] {originalMultiplier: 5, newMultiplier: 10}
// Alternative approach using objects to enable modification
function doubleWithObject(data: {arr: number[], val: number}): void {
function helper(): void {
for (let i = 0; i < data.arr.length; i++) {
data.arr[i] *= 2;
}
data.val *= 2;
}
helper();
console.log(data.arr, data.val);
}
const data = { arr: [1, 2], val: 3 };
doubleWithObject(data); // [2, 4] 6
Classes
class Student {
private names: string[];
private size: number;
// Constructor with parameter properties
constructor(names: string[]) {
this.names = names;
this.size = names.length;
}
// Public method
printNames(): void {
for (const name of this.names) {
console.log(name);
}
}
// Getter method
getLength(): number {
return this.size;
}
// Method calling another method
getDoubleSize(): number {
return 2 * this.getLength();
}
// Getter property
get studentCount(): number {
return this.size;
}
// Setter property
set studentNames(names: string[]) {
this.names = names;
this.size = names.length;
}
}
// Usage
const s = new Student(["Alice", "Bob", "Martha"]);
s.printNames(); // "Alice", "Bob", "Martha"
console.log(s.getDoubleSize()); // 6
console.log(s.studentCount); // 3
// Class with inheritance
class GraduateStudent extends Student {
private graduationYear: number;
constructor(names: string[], graduationYear: number) {
super(names); // Call parent constructor
this.graduationYear = graduationYear;
}
getGraduationInfo(): string {
return `${this.getLength()} students graduating in ${this.graduationYear}`;
}
}
const grad = new GraduateStudent(["Alice", "Bob"], 2025);
console.log(grad.getGraduationInfo()); // "2 students graduating in 2025"
// Interface for type safety
interface Printable {
print(): void;
}
class Document implements Printable {
constructor(private content: string) {}
print(): void {
console.log(this.content);
}
}
// Abstract class
abstract class Shape {
abstract area(): number;
displayArea(): void {
console.log(`Area: ${this.area()}`);
}
}
class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super();
}
area(): number {
return this.width * this.height;
}
}
Resources
- TypeScript Official Documentation
- TypeScript Course for Beginners
- TypeScript Type Challenges
- Total TypeScript
Happy learning!