Typescript
My notes about Typescript.
A 19y/o self-learned ethical hacker, mainly interested in bug hunting, malware analysis, and digital forensics. Currently expertise in SEO, OSINT, ethical hacking, WordPress, Shopify, front-end web.
It’s a superset of JavaScript kind of like JavaScript with superpowers.
Typescript = JavaScript + type-safety
Playground - Try now.
Main Types
String -
let username: string = "dheeraj"Number -
let age: number = 21Boolean -
let isLoggedIn: boolean = trueArray -
let users: string[]Null -
let selectedUser: string | null = null
Explicitly represents “no value”.Undefined -
let token: string | undefined
Value not assigned or not returned.Void - Used when a function does not return anything.
voidis only meaningful for functions, not useful for variables.function logEvent(message: string): void { console.log(message); }Unknown - It is better of any to provide better type safety.
Any - Disables type checking completely.
let response: any; response = "success"; response = 200; response = { ok: true };
There are a few more like Never, Object, Tuples, etc.
Inference and Annotations
Inference - Identifies itself the datatype of given data whether it’s string, int or Boolean, etc.
Typescript handles the type-safety of all default datatypes by itself using type inference.
Ex -let myName = “DJ“
In the above code, we don’t need to define the type of myName as typescript will recognise it as a string itself.Annotations - We need to define the data type manually.
Ex -let myInfo: string = “DJ“
In the above code, we defined thatmyInfois a string. Similarly, we need to define types of all variables, function inputs and outputs.
Union and Any
Union - allows a variable, function parameter, or return value to accept more than one type, providing flexibility while maintaining type safety.
Ex -let birthday: string | number;
In the above code, we can accept both a string or number forbirthday.
We can also use it to define custom type for an element like in below code.
Ex -let apiRequestStatus: ‘pending‘ | ‘success‘ | ‘error‘ = ‘success‘
Here, the value ofapiRequestStatuscan only be success, pending or error.Any - It doesn’t care about what exactly the datatype is. Prefer not to use it anywhere.
Ex -let secret: any
Incase, you are getting as any either as inferred or via annotation, define the type as undefined.Undefined - It represents the absence of a value. It is used to handle missing data like a variable being defined but no value is assigned.
Ex -let user: undefinedtype - It is used to create a custom type.
type User = {
id: string
email: string
isActive: boolean
}
Type Narrowing and Type Guards
Type Narrowing - It says first identify which type or data it is and then based on that, you can return it or process anything based on it.
Typescript often deals with union types, so we use type narrowing to reducing a broad type into a more specific type so TypeScript knows exactly what you’re working with. It helps in getting better support for methods associated with that data type.function print(value: string | number) { if (typeof value === "string") { // value is now string console.log(value.toUpperCase()); } else { // value is now number console.log(value.toFixed(2)); } }Truthiness narrowing is how we figures out that a value is not
null,undefined,false,0,"", orNaNafter you check it in anifcondition. We use it for writing better code by handling elements which don’t return value using if conditions.Type Guards - Type guards are expressions in TypeScript that perform a runtime check which guarantees the type of a variable within a specific scope, helping to eliminate potential runtime errors and improve type safety. They are crucial when working with union types or complex data structures.
Common types of built-in type guards include:
typeof: Checks for primitive types likestring,number,boolean, etc.instanceof: Checks if an object is an instance of a specific class.User-Defined Type Guards (Type Predicates): Custom functions that use a special return type,
parameterName is Type, to narrow the type within a conditional block.Discriminated Unions: Uses a common literal property (the "discriminant") in a union of objects to help TypeScript narrow the type.
Type Assertion and Type Never
Type Assertion - Sometime, we forcefully tell what should be considered the type of that element.
Ex -user as string
Here, we are forcing typescript to evaluate it as a string and give us methods that are available for string.Type Never - It stops us from missing out any edge cases when working with if else. A parameter returns never until somewhere or sometimes we add a new value, it takes that value and handles that edge case.
If a function does not come back, TypeScript marks it asnever.
TypeScript usesneverto catch logic bugs.nevermeans “impossible case”
Types and Interface
Types or Type Alias - creating a custom type of our own.
type User = {name: string, balance: number, is_admin: false}Now we can use this as a type just like string, number, boolean, etc.
For making anything optional, just add ? after the variable like
bio?:stringFor making anything readonly, just add readonly before variable like
readonly credit: numberCombing multiple types using & operator like
type Userinfo = User & Profile
Interface - It’s similar to type but have different syntax and use cases. Used mainly for objects, classes, and APIs. Its objective is to shape your objects.
You can make property optional, set default values and readonly just like we do in types.
They don’t generate any code, they just define the structure.
We use methods to update values.
If we have two interface with same name, we need to satisfy property of both.
Interfaces can be extended by combining two existing interfaces like
interface C extends A, B {}
interface User {
id: string;
email: string;
isActive: boolean;
}
Diff b/w Types and Interface
| Scenario | Use | | --- | --- | | API request / response objects |
interface| | Class contracts |interface| | Union / conditional types |type| | Utility & helper types |type| | Public, extendable models |interface| | Primitive aliases |type|Index Signature - An index signature lets you describe objects where:
You don’t know property names in advance
But you do know the type of the values
type User = {
[key: string]: string;
};
const user: User = {
name: "Dheeraj",
city: "Jaipur",
role: "admin"
};
Generics - Instead of locking a function/class to one type (
string,number, etc.),
you use a placeholder type that gets filled when used.These are similar to interface in usage.
These are mostly used in creating libraries and frameworks.
function identity<T>(value: T): T { return value; } const text = identity<string>("hello"); const num = identity<number>(42); text.toUpperCase(); // ✅ safe num.toFixed(2); // ✅ safeWe can also make generic interface.
interface ApiResponse<T> { success: boolean; data: T; } type User = { name: string; age: number }; type Domain = { domain: string; registrar: string }; const userRes: ApiResponse<User> = { success: true, data: { name: "Dheeraj", age: 22 } }; const domainRes: ApiResponse<Domain> = { success: true, data: { domain: "example.com", registrar: "Namecheap" } };We can also make generic classes.
class Box<T> { private value: T; constructor(v: T) { this.value = v; } get(): T { return this.value; } } const tokenBox = new Box<string>("abc123"); const idBox = new Box<number>(101); tokenBox.get().toUpperCase(); // string methods allowed idBox.get().toFixed(2); // number methods allowedWe can also support Partial, Pick and Omit in generic.
Object
An object is a value that groups related data using key–value pairs.
const user = { name: "Dheeraj", age: 21, };object using interface -
interface User { id: string; email: string; isActive: boolean; } const user: User = { id: "u1", email: "test@mail.com", isActive: true, };objects with methods(functions) -
type User = { id: string; login: () => void; }; const user: User = { id: "u1", login() { console.log("Logged in"); }, };Type Partial - It makes all properties of an object type optional.
type User = { id: string; email: string; isActive: boolean; }; type UpdateUser = Partial<User>; const update: UpdateUser = { email: "new@mail.com", };Type Required - It makes all properties of an object type required even if some has been allowed optional using
?.type User = { id: string; email?: string; isActive?: boolean; }; type StrictUser = Required<User>; const user: StrictUser = { id: "u1", email: "test@mail.com", isActive: true, };Type Pick - It creates a new type using only selected fields from another type.
type User = { id: string; email: string; password: string; isActive: boolean; }; type PublicUser = Pick<User, "id" | "email">; const user: PublicUser = { id: "u1", email: "test@mail.com", };Type Omit - It take an object type and remove some fields from it.
type User = { id: string; email: string; password: string; isActive: boolean; }; type SafeUser = Omit<User, "password">; const user: SafeUser = { id: "u1", email: "test@mail.com", isActive: true, };
Functions
Basic example -
function add(a: number, b: number): number { return a + b; }Always add type void for output to functions that don’t return anything.
function logMessage(message: string): void { console.log(message); }Function with optional parameter -
function greet(name?: string): string { return name ? `Hello ${name}` : "Hello Guest"; }Function with default parameter -
function greet(name: string = "Guest"): string { return `Hello ${name}`; }Function parameter type as object -
type User = { id: string; email: string; }; function sendEmail(user: User): void { console.log(user.email); }
Array, Enum and Tuples
Array - contains same type of items.
basic example -
let names: string[] = ["Alice", "Bob"];let scores: Array<number> = [10, 20, 30];Array of object -
type User = { id: number; name: string; }; let users: User[] = [ { id: 1, name: "Dheeraj" }, { id: 2, name: "Alex" }, ];Read only array -
let roles: readonly string[] = ["admin", "user"];
Tuple - Similar to array but can have different type of items but have same formatting.
let user: [number, string] = [1, "Dheeraj"];Tuple with optional items -
type LogEntry = [message: string, timestamp?: number]; const log1: LogEntry = ["Started"]; const log2: LogEntry = ["Started", Date.now()];Read only tuples -
const Item: readonly [number, boolean, string] = [5, true, 'The Real Coding God'];Named tuples -
type UserTuple = [id: number, name: string, isActive: boolean]; const user: UserTuple = [1, "Dheeraj", true];We can push value in tuples using
.pushmethod because it’s a form of array.
Enum - It is used to restrict the choice. a way to define a fixed set of named constants. It is used when a value must be one of a predefined option.
enum Role { Admin, User, Guest }All const in enum should be capital.
Try to keep same type of data in enum.
Const in enum can have values or they have incremental value if you have defined value of any one.
OOPS Concept
Class - It’s blueprint for creating objects.
Always add constructor when creating classes for best code practices. this keyword in constructor always returns what created that object.class User { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { return `Hello, ${this.name}`; } } const user = new User("Dheeraj", 21); user.greet();access modifier -
Always add access modifier when creating class.
Just add public (accessible everywhere) or private (accessible only inside the class) before any property in class.
If you want to access any private property, you need to use dedicated method reveal for it.
You can also use # instead of private for creating private property.
You can access any protected property in sub class but not outside of classes.
Private property being with a underscore like _mybalance.
class Account {
public username: string;
protected balance: number;
private password: string;
constructor(username: string, password: string, balance: number) {
this.username = username;
this.password = password;
this.balance = balance;
}
public getBalance(): number {
return this.balance;
}
}
class AdminAccount extends Account {
revealBalance(): number {
// ✅ allowed (protected is accessible in subclass)
return this.balance;
}
revealPassword(): string {
// ❌ not allowed
// return this.password;
throw new Error("Cannot access private property");
}
}
const acc = new Account("dheeraj", "secret123", 1000);
readonly property - the property can’t be modified once its value is defined or allocated once.
class Session { readonly id: string; constructor(id: string) { this.id = id; } }getters and setters - when we can’t access properties directly as they have been defined private. we use get method to access them and set method to set their value.
class Account { private _balance = 0; get balance(): number { return this._balance; } set balance(amount: number) { if (amount < 0) throw new Error("Invalid"); this._balance = amount; } }static members - all objects share these properties.
class CreditSystem { static totalCredits = 1000; static deduct(amount: number) { CreditSystem.totalCredits -= amount; } }abstract classes - a base class that cannot be instantiated and is used to define a common structure and behavior that child classes must follow. A class where we don’t want to create objects.
abstract class Vehicle { // abstract method → child MUST implement abstract start(): void; // normal method → shared for all stop() { console.log("Vehicle stopped"); } } class Car extends Vehicle { start() { console.log("Car engine started with key"); } } class Bike extends Vehicle { start() { console.log("Bike started with kick"); } } const car = new Car(); car.start(); // Car engine started with key car.stop(); // Vehicle stopped const bike = new Bike(); bike.start(); // Bike started with kickinheritance - It means one class automatically receives properties and functions of another class.
class User { name: string; constructor(name: string) { this.name = name; } login() { console.log(this.name + " logged in"); } } class Admin extends User { deletePost() { console.log(this.name + " deleted a post"); } } const admin = new Admin("Dheeraj"); admin.login(); // inherited from User admin.deletePost(); // own methodpolymorphism - Different objects respond to the same function in different ways.
class Animal { speak() { console.log("Animal makes sound"); } } class Dog extends Animal { speak() { console.log("Dog barks"); } } class Cat extends Animal { speak() { console.log("Cat meows"); } }
Typescript Declaration Files - A .d.ts file does not contain implementation.
It only describes the shape of code — types, functions, classes, objects — so TypeScript knows how something behaves.
Typescript with React
Try Catch
Switch Case