JS 2 TS
Discovering TypeScript through JavaScript
JavaScript was invented as a scripting language for browsers to add interactivity and dynamic behavior to web pages, since HTML and CSS only provide static structure and styling.
While JavaScript is traditionally an interpreted language, modern browser JavaScript engines compile JavaScript code to machine code for efficient execution. JavaScript is designed to interact within the browser environment, specifically with the Document Object Model (DOM).
1<html>
2  ├── <head>
3  │     ├── <title>
4  │     └── <meta>
5  └── <body>
6        ├── <header>
7        │     └── <h1>
8        ├── <nav>
9        │     └── <ul>
10        │         ├── <li>
11        │         └── <li>
12        └── <main>
13              ├── <section>
14              │     └── <p>
15              └── <footer>
16                    └── <p>
17DOM Structure & Manipulation:
- Structure: The DOM is a tree-like structure representing the HTML of a webpage. Each element, attribute, and text is a node in this tree.
 - Manipulation: JavaScript can access and modify the DOM to change the content, structure, and style of a webpage dynamically, such as adding or removing elements, changing text, or updating CSS styles.
 
Web APIs
Web APIs are built-in interfaces provided by browsers to interact with and manipulate various aspects of the web environment. They enable dynamic UI updates, network requests, graphics and audio processing, real-time communication, data storage, and device feature access (geolocation, battery, file handling, etc.).
JavaScript Frameworks
Frameworks are pre-written libraries that provide a structured way to build and organize web applications. They offer tools, components, and guidelines to streamline development, manage complex tasks, and ensure consistency across projects.
Popular frameworks like React, Angular, and Vue offer different approaches to building JavaScript applications, each with
their own philosophies and strengths.
Beyond the Browser
Even though JavaScript was originally designed for the browser (frontend), it has also gained significant popularity on the backend, proving Jeff Atwood's law:
Any application that can be written in JavaScript, will eventually be written in JavaScript.
~ Jeff Atwood, 2009
Runtime Environments
Runtime environments like Node.js have expanded JavaScript's reach beyond the browser, enabling it to power server-side
applications, desktop applications, and even mobile apps by allowing JavaScript code to execute outside of the browser environment.
JavaScript Syntax
Variables
1let a = 5; // mutable
2const b = 6; // immutable
3Functions
1// function definition
2function add(a, b) {
3    return a + b;
4}
5
6// function expression
7const add = function(a, b) {
8    return a + b;
9}
10
11// arrow function prefered for single line functions or anonymous functions
12const add = (a, b) => a + b;
13Functions are also objects in JavaScript meaning they can:
- Be assigned to variables
 - Be passed as arguments
 - Have properties and methods
 - Be returned from other functions
 
Objects
Objects are collections of key-value pairs.
this keyword that references the current object.
1// object literal syntax
2const person = {
3  name: "Brendan Eich",
4  age: 34,
5  greet: () => {
6    return `Hello, ${this.name}!`;
7  },
8};
9
10person.greet(); // "Hello, Brendan Eich!"
11person.age; // 34
12When variables are passed into functions, they are copied, objects are referenced.
Meaning: when you pass an object into a function, you can modify the object and it will be reflected in the original object.
1const num = 1;
2const obj = new Object();
3
4doSomething(num, obj); // num is copied, obj is referenced
5Object Oriented Programming
class is just a wrapper around the object literal syntax.
constructoris a method that is called when a new object is created.setandgetare used to set and get the value of a property.staticmethods are called on the class itself rather than on an instance of the class.
1class Person {
2  constructor(name, age) {
3    this.name = name;
4    this.age = age;
5  }
6  set country(country) {
7    this.country = country;
8  }
9  get country() {
10    return this.country;
11  }
12  static create(name, age) {
13    return new Person(name, age);
14  }
15  greet() {
16    return `Hello, ${this.name}!`;
17  }
18  static create(name, age) {
19    return new Person(name, age);
20  }
21}
22
23const person = new Person("Brendan Eich", 34);
24person.greet(); // "Hello, Brendan Eich!"
25person.country = "USA";
26person.country; // "USA"
27Arrays
Arrays are collections of values.
1const arr = [1, 2, 3];
2arr[0]; // 1
3arr.push(4); // [1, 2, 3, 4]
4arr.splice(0, 1); // Removes 1 element at index 0
5// arr is now [2, 3, 4]
6arr.length; // 3
7
8const mapped = arr.map((x) => [x, x + 10]);
9// mapped: [[1, 11], [2, 12], [3, 13]]
10
11const flatMapped = arr.flatMap((x) => [x, x + 10]);
12// flatMapped: [1, 11, 2, 12, 3, 13]
13Non-blocking event loop aka. Asynchronous Processing
Usually scripts are executed line by line, but with the event loop we can achieve non-blocking operations. This is needed since websides have multiple processes running at the same time but only access the the Main Thread of the browser.
- Single Thread: javascript uses one main worker thread to handle many tasks simultaneously.
 - Non-blocking I/O: It initiates operations (like file reads or network requests) and continues with other work without waiting for completion.
 - Callbacks & Events: When an operation finishes, javascript processes the result through callbacks or events.
 - Event Loop: A continuous system monitors and manages all pending operations and their completion.
 - Concurrency: This architecture enables handling thousands of concurrent connections without creating additional threads.
 
The loop is basically a queue of tasks to be executed.
1// This callback function will wait 1 second before calling the console.log function.
2setTimeout(() => {
3  console.log("1 second passed");
4}, 1000);
5The better way to handle this is to use a Promise.
AnPromise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
1const promise = new Promise((resolve, reject) => {
2  // Something async here (like api call)
3  if (success) {
4    resolve("Success");
5  } else {
6    reject("Failure");
7  }
8});
9
10// .then() is used to handle the success case
11// .catch() is used to handle the failure case
12promise
13  .then((result) => {
14    console.log(result);
15  })
16  .catch((error) => {
17    console.log(error);
18  });
19Async / Await
asyncfunctions always return a promise.awaitis used to wait for a promise to resolve.
always wrap in a try/catch block.
1async function someAsyncFunction() {
2  try {
3    const result = await somePromise;
4    console.log(result);
5  } catch (error) {
6    console.log(error);
7  }
8}
9
10someAsyncFunction();
11Sharing Code
Modules are used to share code between files.
ES Module syntax: import and export are used to share code between modules.
1// someFile.js
2export const someFunction = () => {
3  console.log("someFunction");
4};
5
6export const someVariable = "someVariable";
7
8export default otherFunction = () => {
9  console.log("otherFunction");
10};
111// main.js
2import { someFunction, someVariable } from "./someFile";
3import otherFunction from "./someFile";
4
5// no curly braces, because it is a default export
6
7someFunction();
8console.log(someVariable);
9Modules are also installed via package manager like npm or pnpm etc.
then they are in the node_modules directory and defined in the package.json file.
Note: there is also a CommonJS syntax for modules (like require and module.exports) but it is getting deprecated.
Control Structure
Logical Operators
&& the logical AND operator is used to check if both conditions are true.
|| the logical OR operator is used to check if at least one condition is true.
! the logical NOT operator is used to check if a condition is false.
!! the logical NOT operator is used to convert a value to a boolean
=== the strict equality operator is used to check if a condition is equal to another condition
>= greater than or equal to another condition
<= less than or equal to another condition
> greater than another condition
< less than another condition
!= not equal to another condition
!== not equal to another condition
?? the nullish coalescing operator is used to check if a condition is null or undefined
1if (condition && condition2) {
2  // code
3} else if (condition || condition2) {
4  // code
5} else if (!condition3) {
6  // code
7} else {
8  // code
9}
10ternary operator is a more concise way to write an if/else statement.
1condition ? true : false;
2switch is a more concise way to write multiple if/else statements.
1switch (expression) {
2  case value is true:
3    // code
4  case value2 === value3:
5    // code
6  default:
7    // code
8}
9Loops
for loop is used to loop a certain number of times.
break is used to break out of a loop.
continue is used to skip the current iteration of a loop.
1for (let i = 0; i < 10; i++) {
2  // i-- would decrement i
3  // code
4  if (i >= 5) {
5    break; // break out of the loop
6  } else if (i <= 3) {
7    continue; // skip the current iteration
8  }
9}
10while loop is used to loop until a condition is met.
1while (condition) {
2  // code
3  if (otherCondition) {
4    break; // break out of the loop
5  }
6}
7TypeScript
Since JavaScript is a dynamically typed language where variables can hold any type of value, TypeScript enhances it by
introducing static typing, allowing developers to catch type-related errors at compile time rather than at runtime.
TypeScript allows you to statically / explicitly define the type of a variable like:
1const greeting: string = "hello world";
2Although you don't have to define the type (as TypeScript will infer the type based on the value assigned to the variable),
you should always define the type of a variable, neglecting this defeats the purpose of TypeScript, same goes for any types
there are little reasons to not define the type of a variable.
Spending time on typing will always pay dividends as the TextEditor will help you with type errors and autocomplete.
1// generally bad idea:
2const greeting: any = "hello world";
3Basic / Primitive Types
1// string
2const greeting: string = "hello world"; // Represents text values.
3
4// bigint
5const bigInt: bigint = 123n; // For arbitrarily large integers.
6
7// number
8const age: number = 42; // Represents both integers and floating-point numbers.
9
10// boolean
11const isLoggedIn: boolean = true; // Represents true/false values.
12
13// undefined
14const undefined: undefined = undefined; // Value automatically assigned to uninitialized variables.
15
16// null
17const null: null = null; // Represents intentional absence of any object value.
18Literal types are types that can only hold a specific value.
1const hello: "hello" = "hello";
2const number5: 5 = 5;
3Union types must be one of the types in the union.
1const phoneNumber: string | number = "+491234567890";
2Object Types
1// object
2// Any non-primitive type (arrays, functions, objects).
3const obj: object = { name: "John", age: 30 };
4
5// Array
6const arr: number[] = [1, 2, 3];
7const arr: string[] = ["1", "2", "3"];
8
9// Tuple
10// Fixed-length, ordered array types.
11const tuple: [number, string] = [1, "2"];
12// Array of tuples
13const tuples: [number, string][] = [
14  [1, "apple"],
15  [2, "banana"],
16  [3, "cherry"]
17];
18
19// Function
20const func: (x: number) => string = (x) => x.toString();
21Generic Types
Generic types are a way to create types that can be used with different types.
1interface Box<T> {
2  value: T;
3}
4
5const stringBox: Box<string> = { value: "banana" };
6const numberBox: Box<number> = { value: 123 };
7
8const stringArray: Array<string> = ["1", "2", "3"];
9Special Types
1// any
2// disables any type checking
3const any: any = "hello";
4
5// unknown
6// safer alternative to any, used for variables that can hold any type
7const unknown: unknown = "hello";
8
9// void
10// use for functions that don't return a value
11const void: void = undefined;
12
13// promise
14// used for asynchronous operations explained earlier
15const promise: Promise<string> = new Promise((resolve, reject) => {
16  resolve("hello");
17});
18Enums
Enums are a way to define a set of named constants.
1enum Direction {
2  Up = "up",
3  Down = "down",
4  Left = "left",
5  Right = "right",
6}
7
8const moveUp: Direction = Direction.Up;
9console.log(moveUp); // "up"
10Type Aliases
Type aliases are a way to give a name to a type.
1type Point = {
2  x: number;
3  y: number;
4};
5
6const point: Point = { x: 1, y: 2 };
7Interfaces
Interfaces describe the shape of objects, mainly for object-oriented design.
1interface Person {
2  name: string;
3  age: number;
4}
5
6interface Employee extends Person {
7  id: number;
8} // use extends for inheritance
9const employee: Employee = { name: "John", age: 30, id: 1 };
10
11interface Person {
12  gender: string;
13} // Adds property to Person
14const person: Person = { name: "John", age: 30, gender: "male" };
15Utility Types
Utility Types are built-in types that can be used to transform other types.
K is the type of the keys, T is the type of the values, meaning you can create a new type with properties of type K and values of type T.
Partial<T>-> Makes all properties optional
1const personName: Partial<Person> = { name: "John" };
2Required<T>-> Makes all properties required
1const completePerson: Required<Person> = {
2  name: "John",
3  age: 30,
4  gender: "male",
5};
6Pick<T, K>-> Picks specific properties from a type
1const personName: Pick<Person, "name"> = { name: "John" };
2Omit<T, K>-> Omits specific properties from a type (opposite ofPick)
1const personWithoutAge: Omit<Person, "age"> = { name: "John", gender: "male" };
2Pick is for objects (selecting properties). Extract is for unions (filtering types).
Extract<T, U>-> Extracts specific properties from a type
1type ABC = "a" | "b" | "c";
2type OnlyA = Extract<ABC, "a">; // "a"
3Exclude<T, U>-> Excludes specific properties from a type (opposite ofExtract)
1type NotA = Exclude<ABC, "a">; // "b" | "c"
2Record<K, T>-> Creates a new type with properties of type K and values of type T
1type FruitCounts = Record<"apple" | "banana" | "cherry", number>;
2
3const fruits: FruitCounts = {
4  apple: 10,
5  banana: 5,
6  cherry: 12,
7  // orange: 3, // Error: 'orange' is not assignable to type 'FruitCounts'
8};
9Type definitions
Generally I like the define types in a separate types.ts file, in the directory
where the types originate from, this allows me the reuse and export the types and
have all the logic revoling around the topic in one place.
1// main.ts
2import { Person } from "./types";
3
4// types.ts
5export type Person = {
6  name: string;
7  age: number;
8};
9
10const person: Person = { name: "John", age: 30 };
11Note: This is compendium is opinionated, some JS and TS charactoeristics like Maps, Sets, Intersection Types... are not covered as I don't find myself using them.