These are essential to build scalable, maintainable, and clean applications:
- Interfaces
- Optional & Readonly Properties
- Functions
- Type Assertions
- Working with Objects
- Narrowing (Type Guards)
- Type Compatibility
- Utility Types
Partial,Pick,Omit,Readonly, etc.
- Working with DOM and Event Handling in TS
- TypeScript with Classes & Inheritance
thisin TypeScript Context
Interfaces define the structure of an object, ensuring type safety.
interface User {
name: string;
age: number;
isAdmin?: boolean; // Optional property
}
const user: User = { name: "Alice", age: 25 };- Use Case: Enforcing consistent object shapes across your application.
Use ? to mark properties as optional.
interface User {
name: string;
age?: number; // Optional
}
const user: User = { name: "Alice" }; // ValidUse readonly to make properties immutable.
interface User {
readonly id: number;
name: string;
}
const user: User = { id: 1, name: "Alice" };
user.id = 2; // β Error: Cannot assign to 'id' because it is a read-only property.Define the shape of a function using types.
type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;- Optional parameters use
?. - Default parameters provide a fallback value.
function greet(name: string, age?: number): string {
return `Hello, ${name}. Age: ${age ?? "unknown"}`;
}
function multiply(a: number, b: number = 2): number {
return a * b;
}Use ... to handle multiple arguments.
function sum(...numbers: number[]): number {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3)); // 6Type assertions tell TypeScript to treat a value as a specific type.
let value: unknown = "Hello";
let strLength: number = (value as string).length;
console.log(strLength); // 5- Use Case: When you know more about a valueβs type than TypeScript does.
TypeScript allows you to define object types and access their properties safely.
type User = {
name: string;
age: number;
};
const user: User = { name: "Alice", age: 25 };
console.log(user.name); // Alice- Index Signatures: Define dynamic property names.
interface Dictionary { [key: string]: string; } const dict: Dictionary = { hello: "world" };
Type guards help narrow down types at runtime.
function printId(id: string | number): void {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed(2));
}
}instanceof: Check if an object is an instance of a class.in: Check if a property exists in an object.
TypeScript uses structural typing, meaning types are compatible if their structures match.
interface Point {
x: number;
y: number;
}
const point: Point = { x: 10, y: 20 }; // Compatible- Use Case: Ensures flexibility while maintaining type safety.
Utility types simplify common type transformations.
Makes all properties optional.
type User = { name: string; age: number };
type PartialUser = Partial<User>;Select specific properties.
type User = { name: string; age: number; isAdmin: boolean };
type Admin = Pick<User, "name" | "isAdmin">;Exclude specific properties.
type User = { name: string; age: number; isAdmin: boolean };
type NonAdmin = Omit<User, "isAdmin">;Makes all properties readonly.
type ReadonlyUser = Readonly<User>;TypeScript provides type definitions for DOM elements and events.
const button = document.querySelector<HTMLButtonElement>("#myButton");
button?.addEventListener("click", (event: MouseEvent) => {
console.log("Button clicked!");
});- Use Case: Ensures type safety when interacting with the DOM.
TypeScript enhances OOP with classes and inheritance.
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet(): void {
console.log(`Hello, ${this.name}`);
}
}
class Student extends Person {
grade: number;
constructor(name: string, grade: number) {
super(name);
this.grade = grade;
}
study(): void {
console.log(`${this.name} is studying.`);
}
}
const student = new Student("Alice", 10);
student.greet(); // Hello, Alice
student.study(); // Alice is studying.The value of this depends on the context in which a function is called.
this refers to the current instance.
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet(): void {
console.log(`Hello, ${this.name}`);
}
}Arrow functions donβt have their own this; they inherit it from the surrounding context.
class Counter {
count = 0;
increment = (): void => {
console.log(this.count++);
};
}
const counter = new Counter();
counter.increment(); // 0
counter.increment(); // 1