TypeScript Notes

This note covers the main TypeScript topics from this codebase with simple explanations, examples, and common use cases.

1. Installation and Setup

npm init -y
npm i -D typescript
npx tsc --init

What each command does:

If tsconfig.json contains "outDir": "./dist", compiled JavaScript files will be created inside the dist folder.

Example scripts:

"scripts": {
  "build": "npx tsc",
  "dev": "npx tsc --watch",
  "start": "node dist/index.js"
}
"scripts": {
  "dev":"tsx watch src/index.ts",
  "build":"tsc",
  "start":"node dist/index.js"
}
{
    "watch":["src"],
    "ext":"ts",
    "exec":"tsx src/server.ts"
}
 "scripts": {
    "dev": "nodemon",
    "build": "tsc",
    "start": "node dist/server.js"
  }

Notes:

For third-party libraries, sometimes you also install types:

npm i -D @types/library-name

Use case:

2. Type Inference

Type inference means TypeScript automatically understands a type from the value you assign.

let score = 100;        // inferred as number
let username = "nishan"; // inferred as string

Why it is useful:

Use case:

3. Type Annotation

Type annotation means you explicitly write the type yourself.

let age: number = 22;
let isLoggedIn: boolean = true;
let city: string = "Kathmandu";

Why it is useful:

Use case:

4. Basic Types

let fullName: string = "Nishan";
let marks: number = 95;
let passed: boolean = true;

Use case:

5. Union Types

A union means a value can be one of multiple types.

let id: string | number;

id = 101;
id = "EMP-101";

Literal unions are also common:

let apiStatus: "success" | "error" | "loading" = "success";

Why it is useful:

Use case:

6. Type Narrowing

Type narrowing means TypeScript starts with a broad type and then narrows it to a smaller, safer type after checks.

function showAge(age: number | string) {
  if (typeof age === "number") {
    return age.toFixed(0);
  }

  return age.toUpperCase();
}

Why it is useful:

Use case:

7. Common Type Guards

Type guards are checks that help TypeScript narrow a type.

typeof

Best for primitive values like string, number, and boolean.

function printValue(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
    return;
  }

  console.log(value.toFixed(2));
}

instanceof

Best for classes.

class AdminUser {
  login() {
    console.log("admin login");
  }
}

class NormalUser {
  signup() {
    console.log("normal signup");
  }
}

function handleUser(user: AdminUser | NormalUser) {
  if (user instanceof AdminUser) {
    user.login();
  }
}

in operator

Best for checking object properties.

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    animal.swim();
    return;
  }

  animal.fly();
}

Truthy and falsy checks

function showMessage(msg?: string) {
  if (msg) {
    return `Message: ${msg}`;
  }

  return "No message provided";
}

User-defined type guard

type PersonalDetails = {
  name: string;
  age: number;
  city: string;
  phone: number;
};

function isPersonalDetails(obj: unknown): obj is PersonalDetails {
  return (
    typeof obj === "object" &&
    obj !== null &&
    "name" in obj &&
    "age" in obj &&
    "city" in obj &&
    "phone" in obj
  );
}

Use case:

8. Custom Type Definition

You can create your own reusable types with type or interface.

Using type

type User = {
  name: string;
  age: number;
  email: string;
};

Using interface

interface Product {
  title: string;
  price: number;
}

Use case:

9. Type Assertion

Type assertion tells TypeScript, "trust me, I know this value's type."

let response: any = "success";
let responseLength: number = (response as string).length;

Another common example:

type Book = {
  name: string;
};

const rawBook = '{"name":"The Great Gatsby"}';
const bookObj = JSON.parse(rawBook) as Book;

Important:

Use case:

Be careful:

10. any vs unknown

any

let value: any = "typescript";
value = 10;
value = [1, 2, 3];
value.toUpperCase(); // no TypeScript error, but can fail at runtime

unknown

let data: unknown = "typescript";

if (typeof data === "string") {
  console.log(data.toUpperCase());
}

Difference:

Use case:

11. Interfaces and Types

Both interface and type can describe object shapes.

interface Admin {
  username: string;
  email: string;
}

type Customer = {
  username: string;
  email: string;
};

Main difference:

Examples where type is stronger:

type Status = "success" | "error";
type Point = [number, number];

Methods inside an interface

interface Singer {
  start(): void;
  stop(): void;
}

Index signature

interface StringDictionary {
  [key: string]: string;
}

Interface merging

interface UserAccount {
  email: string;
}

interface UserAccount {
  password: string;
}

After merging:

const user: UserAccount = {
  email: "[email protected]",
  password: "secret"
};

Interface extends

interface A {
  a: string;
}

interface B {
  b: string;
}

interface C extends A, B {
  c: number;
}

Can a class implement a type?

Yes, a class can implement:

Example:

type PersonShape = {
  name: string;
  greet(): void;
};

class Person implements PersonShape {
  constructor(public name: string) {}

  greet() {
    console.log(`Hello, ${this.name}`);
  }
}

A class cannot implement a union like this:

type Result = "success" | "error";

That is because a union is not a single object contract.

12. Objects in TypeScript

type UserProfile = {
  readonly id: number;
  name: string;
  email?: string;
};

const profile: UserProfile = {
  id: 1,
  name: "Nishan"
};

Concepts:

Use case:

13. Intersection Types with Optional Fields

Intersection combines multiple types into one.

type ContactInfo = {
  email?: string;
  phone?: string;
};

type EmployeeInfo = {
  id: number;
  name: string;
};

type EmployeeProfile = EmployeeInfo & ContactInfo;

const employee: EmployeeProfile = {
  id: 1,
  name: "Nishan",
  email: "[email protected]"
};

Use case:

14. Utility Types

TypeScript has built-in utility types that save time.

Partial<T>

Makes all properties optional.

type Book = {
  title: string;
  price: number;
};

function updateBook(updates: Partial<Book>) {
  console.log(updates);
}

Use case:

Required<T>

Makes all properties required.

type DraftUser = {
  name?: string;
  email?: string;
};

type FinalUser = Required<DraftUser>;

Use case:

Pick<T, K>

Selects only some properties.

type Nishan = {
  name: string;
  age: number;
  city: string[];
};

type NishanBasic = Pick<Nishan, "name" | "age">;

Use case:

Omit<T, K>

Removes some properties.

type NishanWithoutCity = Omit<Nishan, "city">;

Use case:

15. Functions

Always type function parameters, and usually type return values for important functions.

function add(a: number, b: number): number {
  return a + b;
}

Optional parameter:

function order(type?: string) {
  if (type) {
    return `You ordered a ${type} pizza.`;
  }

  return "You ordered a pizza.";
}

Use case:

16. Arrays

Normal array syntax

const names: string[] = ["nishan", "dhakal"];
const numbers: number[] = [1, 2, 3, 4, 5];

Generic array syntax

const ratings: Array<number> = [3, 4, 4.5, 5];

Array of objects

type Order = {
  name: string;
  price: number;
};

const orders: Order[] = [
  { name: "pizza", price: 10 },
  { name: "burger", price: 5 },
  { name: "pasta", price: 8 }
];

Use case:

17. Tuples

Tuples are fixed-length arrays where each position has a specific type.

let user: [string, number, boolean?];

user = ["nishan", 22];
user = ["nishan", 22, true];

Named tuple example:

let bottle: [price: number, size: "small" | "medium" | "large"];
bottle = [10, "medium"];

Use case:

18. Enum

Enums let you define a group of named constant values.

enum Size {
  Small,
  Medium,
  Large
}

let bottleSize: Size = Size.Medium;

Use case:

19. OOP in TypeScript

Class and object

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  display() {
    console.log(`Name: ${this.name}, Age: ${this.age}`);
  }
}

const p1 = new Person("Nishan", 22);
p1.display();

What is this?

Inside a class method, this usually refers to the current object instance.

this.name
this.age

What is an instance?

An instance is the actual object created from a class blueprint.

const p1 = new Person("Nishan", 22);

Here p1 is an instance of Person.

Access modifiers

class Employee {
  public name: string;
  private empId: number;
  protected department: string;

  constructor(name: string, empId: number, department: string) {
    this.name = name;
    this.empId = empId;
    this.department = department;
  }
}

Meaning:

Getter and setter

class Bottle {
  private _capacity: number;

  constructor(capacity: number) {
    this._capacity = capacity;
  }

  get capacity(): number {
    return this._capacity;
  }

  set capacity(value: number) {
    if (value <= 0) {
      throw new Error("Capacity must be positive");
    }

    this._capacity = value;
  }
}

Use case:

Static members

Static members belong to the class itself, not to each object.

class MathUtils {
  static PI = 3.14;
}

console.log(MathUtils.PI);

Use case:

Abstract class

An abstract class is a base class that cannot be created directly and can force child classes to implement methods.

abstract class Drink {
  abstract serve(): void;
}

class Tea extends Drink {
  serve(): void {
    console.log("Serving tea");
  }
}

Use case:

20. Generics

Generics let you write reusable code while keeping type safety.

Generic function

function wrapInArray<T>(value: T): T[] {
  return [value];
}

wrapInArray(5);
wrapInArray("hello");
wrapInArray({ name: "Nishan", age: 20 });

Multiple generic types

function pair<T, U>(a: T, b: U): [T, U] {
  return [a, b];
}

Generic interface

interface Box<T> {
  value: T;
}

const numberBox: Box<number> = {
  value: 123
};

Generic API response

interface ApiResponse<T> {
  status: number;
  data: T;
}

const response: ApiResponse<{ msg: string }> = {
  status: 200,
  data: { msg: "success" }
};

Use case:

21. Typed Web Requests

TypeScript is very useful when calling APIs.

import axios, { type AxiosResponse } from "axios";

interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

const apiResponse: AxiosResponse<Todo> = await axios.get(
  "https://jsonplaceholder.typicode.com/todos/1"
);

Why it is useful:

Use case:

22. Best Practices

23. Quick Revision Summary

This file is meant to be a simple reference. You can keep adding your own examples from the src folder as you learn more.