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>
17
DOM 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
3
Functions
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;
13
Functions
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
12
When 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
5
Object Oriented Programming
class
is just a wrapper around the object literal syntax.
constructor
is a method that is called when a new object is created.set
andget
are used to set and get the value of a property.static
methods 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"
27
Arrays
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]
13
Non-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);
5
The 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 });
19
Async / Await
async
functions always return a promise.await
is 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();
11
Sharing 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};
11
1// 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);
9
Modules 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}
10
ternary operator
is a more concise way to write an if/else statement.
1condition ? true : false;
2
switch
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}
9
Loops
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}
10
while
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}
7
TypeScript
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";
2
Although 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";
3
Basic / 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.
18
Literal types are types that can only hold a specific value.
1const hello: "hello" = "hello";
2const number5: 5 = 5;
3
Union types must be one of the types in the union.
1const phoneNumber: string | number = "+491234567890";
2
Object 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();
21
Generic 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"];
9
Special 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});
18
Enums
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"
10
Type 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 };
7
Interfaces
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" };
15
Utility 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" };
2
Required<T>
-> Makes all properties required
1const completePerson: Required<Person> = {
2 name: "John",
3 age: 30,
4 gender: "male",
5};
6
Pick<T, K>
-> Picks specific properties from a type
1const personName: Pick<Person, "name"> = { name: "John" };
2
Omit<T, K>
-> Omits specific properties from a type (opposite ofPick
)
1const personWithoutAge: Omit<Person, "age"> = { name: "John", gender: "male" };
2
Pick 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"
3
Exclude<T, U>
-> Excludes specific properties from a type (opposite ofExtract
)
1type NotA = Exclude<ABC, "a">; // "b" | "c"
2
Record<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};
9
Type 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 };
11
Note: 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.