Mission 1: Be a typescript technocrat
Tags: typescript, javascript, tutorial, webdev
Published: 2023-11-03 16:06:08

_
**

# Module-1: Explore basic types of typescript
**
## **1-2 install typescript and node version manager in **ubuntu** **
_
To install TypeScript and Node Version Manager (nvm) on your Ubuntu Linux system, you can follow these steps:

**1. Install Node Version Manager (nvm):**

First, open your terminal and run the following commands to install nvm:

```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
```

This command will download and execute the nvm installation script.

After the installation is complete, close and reopen your terminal, or run:

```bash
source ~/.bashrc
```

This will load nvm into your current shell session.

**. Install Node.js and TypeScript:**

Now that nvm is installed, you can use it to install Node.js and TypeScript. Here's how to install the latest LTS version of Node.js and TypeScript:

```bash
nvm install --lts
```

This command installs the latest LTS (Long Term Support) version of Node.js. Once Node.js is installed, you can install TypeScript using npm (Node Package Manager), which comes bundled with Node.js:

```bash
npm install -g typescript
```

This command installs TypeScript globally on your system.

**3. Verify the installations:**

You can verify that both Node.js and TypeScript have been installed by checking their versions:

```bash
node -v
npm -v
tsc -v
```

This should display the version numbers of Node.js, npm, and TypeScript.

That's it! You've successfully installed Node.js and TypeScript on your Ubuntu Linux system using nvm. You can now start developing TypeScript applications with Node.js.

#
_**If you want to switch to Node.js version 14 for your previous projects using Node Version Manager (nvm), you can follow these steps:**_

**1. Check available Node.js versions:**

To see the list of Node.js versions available for installation, you can use the following command:

```bash
nvm ls-remote
```

This will list all the available Node.js versions. You can look for version 14 in the list.

**2. Install Node.js version 14:**

To install Node.js version 14, use the following command, replacing "14" with the version you want:

```bash
nvm install 14
```

This will download and install Node.js version 14.

**3. Set Node.js version 14 as the default:**

To set Node.js version 14 as the default version for your projects, use the following command:

```bash
nvm use 14
```

This will switch your current shell session to use Node.js version 14. If you want to make it the default for all new shell sessions, you can run:

```bash
nvm alias default 14
```

Now, Node.js version 14 is the default version, and you can use it for your previous projects.

**4. Verify the Node.js version:**

You can verify that Node.js version 14 is in use by running:

```bash
node -v
```

This should display the version of Node.js, which should be 14.x.x if the installation was successful.

That's it! You've now switched to Node.js version 14 using nvm, and you can use it for your previous projects.

#
_**To switch back to Node.js version 20.9.0 or any other version you were using, you can use Node Version Manager (nvm). Here are the steps to switch to version 20.9.0:**_

1. List the available Node.js versions to see which versions are installed and available for use:

```bash
nvm ls
```

This will list the installed Node.js versions, and the currently active version will be indicated with an arrow.

2. To switch to Node.js version 20.9.0, use the following command, replacing "20.9.0" with the specific version you want:

```bash
nvm use 20.9.0
```

This command will set the active Node.js version to 20.9.0.

3. You can verify that you've switched to Node.js version 20.9.0 by running:

```bash
node -v
```

This should display the version number, which should be 20.9.0 if the switch was successful.

Now you are using Node.js version 20.9.0 for your projects. If you want to make this version the default for all new shell sessions, you can run:

```bash
nvm alias default 20.9.0
```

This will set version 20.9.0 as the default for new sessions.

You can use these commands to switch between different Node.js versions whenever you need to work on specific projects that require different Node.js versions.

============================================================

## 1-3 Write your first typescript program


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fuzzqy5o9gvja4e1wtqz.png)

> Typescript config file
: **tsc --init**
then we ill get a tsconfig.json file, then we will edit it:

1. search rootDir; then write typescritp file location in this rootDir

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kv7afzqdprwhxzi573t.png)

2. search outDir, write path of my typescript file will store in which location.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jnnfvuids0rfjei5jx3c.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v3mg9falp1hj3lcte6wo.png)

after that write tsc. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p73dhipd0egkpnius8u9.png)

## some extra package. to run typescript code directly
**1. Install ts-node-dev globally (recommended):** 

```
npm install -g ts-node-dev
```
**2. Use npx (without installing globally):**

```
ts-node-dev --respawn --transpile-only filelocation/filename.ts
```

```
npx ts-node-dev --respawn --transpile-only ./module1/src/index.ts

```
## 1-4 Basic data types

Certainly! Here are examples of various data types in TypeScript:

1. **Integer (int)**:

   ```typescript
   const age: number = 30;
   ```

2. **Floating-Point (float and double)**:

   ```typescript
   const pi: number = 3.14159;
   ```

3. **Boolean (bool)**:

   ```typescript
   const isTrue: boolean = true;
   ```

4. **Character (char)**:

   In TypeScript, characters are represented as single-character strings:

   ```typescript
   const firstLetter: string = "A";
   ```

5. **String**:

   ```typescript
   const greeting: string = "Hello, TypeScript!";
   ```

6. **Null**:

   ```typescript
   const nullValue: null = null;
   ```

7. **Undefined (or None)**:

   ```typescript
   let undefinedValue: undefined;
   ```

8. **Void**:

   Functions with no return value have a `void` type:

   ```typescript
   function sayHello(): void {
     console.log("Hello, world!");
   }
   ```

9. **Symbol**:

   ```typescript
   const uniqueSymbol: symbol = Symbol("description");
   ```

10. **Byte**:

    TypeScript doesn't have a specific byte data type, but you can work with bytes using `number` or `Uint8Array`.

11. **Date and Time**:

    TypeScript can work with date and time using JavaScript's `Date` object:

    ```typescript
    const currentDate: Date = new Date();
    ```

12. **BigInteger and BigDecimal**:

    TypeScript supports large integers through the `BigInt` type:

    ```typescript
    const bigIntValue: bigint = 1234567890123456789012345678901234567890n;
    ```

13. **Array**:

    Arrays in TypeScript can hold multiple values of the same data type:

    ```typescript
    const numbers: number[] = [1, 2, 3, 4, 5];
    ```

14. **Tuple**:

    Tuples allow you to specify the types and order of elements in a fixed-size collection:

    ```typescript
    const person: [string, number] = ["Alice", 30];
    ```

15. **Enum**:

    Enums define a set of named constant values:

    ```typescript
    enum Color {
      Red,
      Green,
      Blue,
    }

    const selectedColor: Color = Color.Red;
    ```

16. **Any**:

    The `any` type is used when you want to opt out of TypeScript's type checking or when dealing with dynamic types:

    ```typescript
    let dynamicValue: any = 42;
    dynamicValue = "Hello, World!";
    ```

17. **Union Types**:

    Union types allow variables to hold values of multiple types:

    ```typescript
    let mixedValue: number | string = 42;
    mixedValue = "Hello, World!";
    ```

18. **Intersection Types**:

    Intersection types combine multiple types into one, allowing access to properties of each type:

    ```typescript
    type Employee = { name: string; role: string };
    type Manager = { reports: number };
    type ManagerEmployee = Employee & Manager;
    const manager: ManagerEmployee = { name: "Alice", role: "Manager", reports: 5 };
    ```

19. **Object**:

    The `object` type represents non-primitive values, such as functions, arrays, and objects:

    ```typescript
    const user: object = { name: "Alice", age: 30 };
    ```

20. **Function**:

    Functions in TypeScript can be defined with specific parameter and return types:

    ```typescript
    const add: (x: number, y: number) => number = (x, y) => x + y;
    ```

These examples cover a wide range of basic data types and type annotations available in TypeScript. Each type has its own specific use cases, and TypeScript's strong type system helps catch type-related errors and improve code quality.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nob7iinzp1bvecni40i.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9o0uh6m0xkli6zm4kxen.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujcxn1qm8k6pmf82la56.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wubt1xrzffahisffppti.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/todj12qj6ae9gyfp9ckg.png)
----------------------------------------
In TypeScript, you can work with various basic data types, including:
These are the basic data types in TypeScript. Additionally, TypeScript supports more advanced types such as `object`, `any`, `void`, `null`, and `undefined`, as well as user-defined types like `enum`, `tuple`, and `union types` to handle a wide range of data in a strongly typed manner.

1. **Number**: Used for numeric values (integers and floating-point numbers).
   ```typescript
   let age: number = 30;
   let pi: number = 3.14;
   ```

2. **String**: Used for text and character data.
   ```typescript
   let name: string = "John";
   let message: string = 'Hello, TypeScript!';
   ```

3. **Boolean**: Represents true or false values.
   ```typescript
   let isStudent: boolean = true;
   let isWorking: boolean = false;
   ```

4. **Array**: Used to store collections of values, and you can specify the type of elements it contains.
   ```typescript
   let numbers: number[] = [1, 2, 3, 4, 5];
   let fruits: string[] = ["apple", "banana", "cherry"];
   ```
-----------------
## 1-5 Object , Optional and Literal
Here are examples of TypeScript data types and features related to objects, optional properties, and literals:

1. **Object Type**:

   TypeScript allows you to define object types to represent structured data with specific properties and their types.

   ```typescript
   const person: { name: string; age: number } = {
     name: "Alice",
     age: 30,
   };
   ```

2. **Optional Properties**:

   You can specify optional properties in object types using the `?` symbol.

   ```typescript
   const user: { name: string; age?: number } = {
     name: "Bob",
     // Age is optional, so it can be omitted
   };
   ```

3. **Literal Types**:

   TypeScript allows you to define literal types where a variable can only have a specific, literal value.

   ```typescript
   const status: "active" | "inactive" = "active";
   ```

4. **Object with Optional and Literal Properties**:

   You can combine object types, optional properties, and literal types.

   ```typescript
   const person: { name: string; age?: number; status: "active" | "inactive" } = {
     name: "Charlie",
     status: "active",
   };
   ```

5. **Type Alias**:

   You can create type aliases for object types to make your code more readable.

   ```typescript
   type Person = { name: string; age?: number };
   const user: Person = {
     name: "David",
     // Age is optional
   };
   ```

6. **Interface**:

   Interfaces are similar to type aliases but are often used when defining the shape of objects.

   ```typescript
   interface Product {
     name: string;
     price: number;
   }
   const item: Product = {
     name: "Widget",
     price: 10.99,
   };
   ```

7. **Record Type**:

   You can use the `Record` type to define object types with specific keys and their associated value types.

   ```typescript
   const phonebook: Record<string, string> = {
     Alice: "555-1234",
     Bob: "555-5678",
   };
   ```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fy97gdtb1gpkrdmf24cs.png)
=>Readonly property

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trxdeswqj92shcgre2c2.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9wvkzulb7rqnb95grnys.png)

## 1-6 Function in typescript

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lo6kz1ultrvx17v1scs.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n4gc4fcej1dxynuwworp.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/armhcgny2rt11ifd9ocf.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s55mbxnpaa3i0850wxrc.png)

## 1-7 Spread and Rest Operator
Certainly! Here are examples of the spread and rest operators in TypeScript:

1. **Spread Operator**:

   The spread operator, denoted by `...`, is used to split an array or object into individual elements. It can be used to create new arrays or objects by combining existing ones.

   - **Array Spread**:

     ```typescript
     const arr1: number[] = [1, 2, 3];
     const arr2: number[] = [4, 5, 6];
     const combinedArray: number[] = [...arr1, ...arr2];
     ```

   - **Object Spread**:

     ```typescript
     const person = { name: "Alice", age: 30 };
     const additionalInfo = { city: "Wonderland" };
     const combinedPerson = { ...person, ...additionalInfo };
     ```

2. **Rest Operator**:

   The rest operator, also denoted by `...`, is used to collect multiple values into an array. It is often used in function parameters to handle a variable number of arguments.

   - **Rest Parameters in Functions**:

     ```typescript
     function sum(...numbers: number[]): number {
       return numbers.reduce((total, num) => total + num, 0);
     }

     const result1: number = sum(1, 2, 3, 4, 5); // Result: 15
     ```

   - **Rest Elements in Arrays**:

     ```typescript
     const [first, second, ...rest] = [1, 2, 3, 4, 5];
     console.log(first); // 1
     console.log(second); // 2
     console.log(rest); // [3, 4, 5]
     ```

{
typescript can acess all function globally. so we can use {bracket} in a ts file, then wont access same function globally.
}

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvl1t1p4wbco9ikncuy9.png)



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hc04dgtlso947xz9uryf.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v2b016imzcuoa8n3gbf4.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e13bzrt5eohf131ubm67.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhzsl24epsi8byoy1dq6.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jk16986s25rs7iefzseq.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jxaxodww2ccdqdn80uvc.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjm6460p5vhsjbugbk4x.png)

## 1-8 Destructuring in typescript
Destructuring is a powerful feature in TypeScript that allows you to extract values from arrays and objects and assign them to variables. Here are examples of destructuring in TypeScript:

1. **Array Destructuring**:

   You can destructure arrays to extract individual values and assign them to variables:

   ```typescript
   const numbers: number[] = [1, 2, 3, 4, 5];

   const [first, second, ...rest] = numbers;

   console.log(first); // 1
   console.log(second); // 2
   console.log(rest); // [3, 4, 5]
   ```

2. **Object Destructuring**:

   Object destructuring allows you to extract properties from objects and assign them to variables with the same name:

   ```typescript
   const person = {
     name: "Alice",
     age: 30,
     city: "Wonderland",
   };

   const { name, age } = person;

   console.log(name); // "Alice"
   console.log(age); // 30
   ```

3. **Renaming Variables in Object Destructuring**:

   You can also rename variables while destructuring objects:

   ```typescript
   const person = {
     name: "Bob",
     age: 25,
   };

   const { name: fullName, age: years } = person;

   console.log(fullName); // "Bob"
   console.log(years); // 25
   ```

4. **Default Values in Object Destructuring**:

   You can provide default values for object properties that might not exist:

   ```typescript
   const person = {
     name: "Charlie",
   };

   const { name, age = 30 } = person;

   console.log(name); // "Charlie"
   console.log(age); // 30
   ```

5. **Nested Destructuring**:

   Destructuring can also be used with nested objects:

   ```typescript
   const student = {
     name: "David",
     details: {
       age: 21,
       major: "Computer Science",
     },
   };

   const { name, details: { age, major } } = student;

   console.log(name); // "David"
   console.log(age); // 21
   console.log(major); // "Computer Science"
   ```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnrg9l1supwnejh7v1mz.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bk3l9ywpdwgc1uocp1oa.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nbmaa0m7f4fjyxqj3nc8.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bcz4078udoibz9afcx6a.png)

## ...rest

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czsbrxsxtdumxk1rly4b.png)

## 1-9 Type alias in typescript


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x41ykmgzsb607cbdjiir.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qwjemrhkcmox75qqr46.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3f8ej8s2bnt9sxo1sbnm.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ls7b7jxj6diue24ucsdw.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mirhhxudof4p55nqtkgi.png)

## 1-10 Union and Intersection types

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2a0pm9kh0aohkpw9oko.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/alfx80eql7iwu4nsw1it.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v25kr7vgas4no8785et1.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmjgwvw6zqtmarb218xb.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyylf3vl0508vgjf8jyw.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j6x1cu9s562oatsykw8r.png)

## 1-11 Ternary, optional chaining & nullish coalescing operator

**1. nullish operator(??)** its works for Null and undefined. not for "" .
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ys683pqvg45my7v7nedm.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shxyjj1yyw2klu8chw2z.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udqm9nmoq918nu1jltui.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rblftc0jde57koua7d9q.png)

## 1-12 Never,unknown and nullable type
Certainly! Here are examples of TypeScript data types and features related to `never`, `unknown`, and nullable types:

1. **`never` Type**:

   The `never` type represents values that never occur. It is often used to indicate that a function will never return normally or that a variable cannot have a value.

   ```typescript
   function throwError(message: string): never {
     throw new Error(message);
   }

   function infiniteLoop(): never {
     while (true) {
       // Infinite loop
     }
   }
   ```

2. **`unknown` Type**:

   The `unknown` type is a type-safe counterpart to `any`. Variables of type `unknown` can hold values of any type, but you must perform type checks before using them.

   ```typescript
   let userInput: unknown;
   let username: string;

   userInput = "Alice";
   if (typeof userInput === "string") {
     username = userInput;
   }
   ```

3. **Nullable Types**:

   TypeScript allows you to make a type nullable using the `null` and `undefined` values.

   ```typescript
   let numberOrNull: number | null = null;
   let stringOrUndefined: string | undefined = "Hello";
   ```

4. **Using `null` and `undefined` Together**:

   You can create a type that allows both `null` and `undefined` values.

   ```typescript
   let value: number | null | undefined = null;
   ```

5. **Type Assertion for `unknown`**:

   When working with `unknown`, you can use type assertions to tell TypeScript to treat a value as a specific type.

   ```typescript
   let userInput: unknown = "Bob";
   let username: string = userInput as string;
   ```

6. **Strict Null Checks**:

   TypeScript's strict null checks prevent variables from being used before being checked for null or undefined, reducing the risk of runtime errors.

   ```typescript
   let possiblyNull: string | null = null;
   // Error: Object is possibly 'null'.
   console.log(possiblyNull.length);
   ```

7. **Non-Nullable Assertion**:

   You can use the non-nullable assertion operator `!` to tell TypeScript that a value is not null or undefined.

   ```typescript
   let definitelyNotNull: string | null = "Carol";
   let length: number = definitelyNotNull!.length; // No error
   ```

These examples showcase TypeScript's `never`, `unknown`, and nullable types, which provide more control over types, safer code, and improved handling of optional and potentially missing values.
1. Nullable type
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ey8swlarvh5yis0mt9v.png)
2. unknown type
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4pnl9qx2rbvkdyxbxnf3.png)
3.Never

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/md6vqkevus3ugy0bafiq.png)

## ========================================================================

**## Module 2:  Explore advance types of typescript
**

## 2-1: Type assertion / type narrowing
=> **`as`** use korle typescript sure hoy je ami je type a declare korbo shei type er e hobe.
```typescript
{
  //

  // type assertion
  let anything: any;

  anything = "Next Level Web Development";

  anything = 222;

  // (anything as number).

  const kgToGm = (value: string | number): string | number | undefined => {
    if (typeof value === "string") {
      const convertedValue = parseFloat(value) * 1000;
      return `The converted value is : ${convertedValue}`;
    }
    if (typeof value === "number") {
      return value * 1000;
    }
  };

  const result1 = kgToGm(1000) as number;
  const result2 = kgToGm("1000");

  type CustomError = {
    message: string;
  };

  try {
  } catch (error) {
    console.log((error as CustomError).message);
  }

  //
}
```

## 2-2: Interface, type vs interface
`=> type ke interface a extend and interface ke type a extend kora jabe.`

```typescript
  // interface

  type User1 = {
    name: string;
    age: number;
  };

  type UserWithRole1 = User1 & { role: string };

  const user1: UserWithRole1 = {
    name: "Persian",
    age: 100,
    role: "manager",
  };
```

```typescript
  interface User2 {
    name: string;
    age: number;
  }

  interface UserWithRole2 extends User2 {
    role: string;
  }

  const user1: UserWithRole2 = {
    name: "Persian",
    age: 100,
    role: "manager",
  };
```

```ts
  type rollNumber = number;

  // js --> object , array-> object function -> object

  type Roll1 = number[];
  const rollNumber1: Roll2 = [1,2,3]

  interface Roll2 {
   [index : number ] : number
  }
  const rollNumber1: Roll2 = [1,2,3]
                              0 1 2 --> number type
```


```ts
  type Add1 = (num1: number,num2:number)=> number
  const add: Add1 = (num1 , num2 )=> num1+num2

  interface Add2 {
    (num1: number,num2:number) : number
  }
  const add: Add2 = (num1 , num2 )=> num1+num2

```

## 2-3: Introduction to generics

```ts
{
  // generic type

  type GenericArray<T> = Array<T>;

  // const rollNumbers: number[] = [3, 6, 8];
  const rollNumbers: GenericArray<number> = [3, 6, 8];

  // const mentors: string[] = ["Mr. X", "Mr. Y", "Mr. Z"];
  const mentors: GenericArray<string> = ["Mr. X", "Mr. Y", "Mr. Z"];

  // const boolArray: boolean[] = [true, false, true];
  const boolArray: GenericArray<boolean> = [true, false, true];

  interface User {
    name: string;
    age: number;
  }

  const user: GenericArray<User> = [
    {
      name: "Mezba",
      age: 100,
    },
    {
      name: "Jhankar Mahbub",
      age: 110,
    },
  ];

  const add = (x: number, y: number) => x + y;

  add(30, 20);

  //generic tuple

  type GenericTuple<X, Y> = [X, Y];

  const manush: GenericTuple<string, string> = ["Mr. X", "Mr. Y"];

  const UserWithID: GenericTuple<number, { name: string; email: string }> = [
    1234,
    { name: "persian", email: "a@gmail.com" },
  ];
}
```

```ts
// Generic tuple
type GenericTuple<X, Y> = [X, Y];

const manush: GenericTuple<string, string> = ["Mr. X", "Mr. Y"];
const UserWithID: GenericTuple<number, { name: string; email: string }> = [1234, { name: "persian", email: "a@gmail.com" }];

console.log(manush); // Output: ["Mr. X", "Mr. Y"]
console.log(UserWithID); // Output: [1234, { name: "persian", email: "a@gmail.com" }]
=================
// Non-generic tuple types
type StringTuple = [string, string];
type UserWithIDTuple = [number, { name: string; email: string }];

const manush: StringTuple = ["Mr. X", "Mr. Y"];
const UserWithID: UserWithIDTuple = [1234, { name: "persian", email: "a@gmail.com" }];

console.log(manush); // Output: ["Mr. X", "Mr. Y"]
console.log(UserWithID); // Output: [1234, { name: "persian", email: "a@gmail.com" }]


```

## 2-4: Generic with Interface

```ts
{
  // intercae - generic

  interface Developer<T, X = null> {
    name: string;
    computer: {
      brand: string;
      model: string;
      releaseYear: number;
    };
    smartWatch: T;
    bike?: X;
  }

  type EmilabWatch = {
    brand: string;
    model: string;
    display: string;
  };

  const poorDeveloper: Developer<EmilabWatch> = {
    name: "Persian",
    computer: {
      brand: "Asus",
      model: "X-255UR",
      releaseYear: 2013,
    },
    smartWatch: {
      brand: "Emilab",
      model: "kw66",
      display: "OLED",
    },
  };

  interface AppleWatch {
    brand: string;
    model: string;
    heartTrack: boolean;
    sleepTrack: boolean;
  }

  interface YamahaBike {
    model: string;
    engineCapacity: string;
  }

  const richDeveloper: Developer<AppleWatch, YamahaBike> = {
    name: "Rich Dev",
    computer: {
      brand: "HP",
      model: "X-25UR",
      releaseYear: 2018,
    },
    smartWatch: {
      brand: "Apple Watch",
      model: "Something",
      heartTrack: true,
      sleepTrack: true,
    },
    bike: {
      model: "Yamaha",
      engineCapacity: "100cc",
    },
  };

  //
}

```

## 2-5: Function with generics

```ts
{
  // function with generics

  const createArray = (param: string): string[] => {
    return [param];
  };

  const createArrayWithGeneric = <T>(param: T): T[] => {
    return [param];
  };

  const res1 = createArray("Bangladesh");
  const resGeneric = createArrayWithGeneric<string>("Bangladesh");

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

  const resGenericObj = createArrayWithGeneric<User>({
    id: 222,
    name: "Mr. Pashan",
  });

  const createArrayWithTuple = <T, Q>(param1: T, param2: Q): [T, Q] => {
    return [param1, param2];
  };

  const res10 = createArrayWithTuple<string, number>("Bangladesh", 222);
  const res11 = createArrayWithTuple<string, { name: string }>("Bangladesh", {
    name: "Asia",
  });

  const addCourseToStudent = <T>(student: T) => {
    const course = "Next Level Web Development";
    return {
      ...student,
      course,
    };
  };

  const student1 = addCourseToStudent({
    name: "Mr X",
    email: "x@gmail.com",
    devType: "NLWD",
  });

  const student2 = addCourseToStudent({
    name: "Mr Y",
    email: "y@gmail.com",
    hasWatch: "Apple Watch",
  });

  //
}

```

## 2-6: Constraints in typescript

```ts
{
  // constraints

  const addCourseToStudent = <
    T extends { id: number; name: string; email: string }
  >(
    student: T
  ) => {
    const course = "Next Level Web Development";
    return {
      ...student,
      course,
    };
  };

  const student3 = addCourseToStudent({
    id: 44,
    name: "Mr. Z",
    email: "z@gmail.com",
    emni: "emni",
  });

  const student1 = addCourseToStudent<{
    id: number;
    name: string;
    email: string;
    devType: string;
  }>({
    id: 222,
    name: "Mr X",
    email: "x@gmail.com",
    devType: "NLWD",
  });

  const student2 = addCourseToStudent({
    id: 333,
    name: "Mr Y",
    email: "y@gmail.com",
    hasWatch: "Apple Watch",
  });

  //
}
```

## 2-7: Constraint using key of
The `keyof` operator is useful in TypeScript for several reasons:

1. **Type Safety**: It enforces type safety by allowing you to restrict the set of possible keys that can be used when accessing properties of an object. This helps catch potential runtime errors at compile time.

2. **Code Completion and Documentation**: It enhances the development experience by providing code editors with information about the available keys, enabling better code completion and documentation.

3. **Improved Refactoring**: When you use `keyof`, if you rename a property in the object type, your code editor can help you identify and update all the places where that property is accessed, ensuring that your refactoring is more reliable.

4. **Reduced Error Handling**: By limiting the possible keys, you can reduce the need for extensive error handling because TypeScript can catch invalid key accesses at compile time. This makes your code more robust and less prone to runtime errors.

Here's an example to illustrate the benefits:

```typescript
type User = {
  name: string;
  email: string;
};

function getUserProperty<T, K extends keyof T>(user: T, key: K): T[K] {
  return user[key];
}

const user: User = {
  name: "Alice",
  email: "alice@example.com",
};

const name: string = getUserProperty(user, "name"); // Valid
const invalidKey = getUserProperty(user, "age"); // Error: Argument of type '"age"' is not assignable to parameter of type '"name" | "email"'.
```

In the example above, using `keyof` with the `getUserProperty` function ensures that only valid keys (in this case, "name" or "email") can be used to access properties of the `User` object. This helps catch potential errors during development and provides a better development experience.

Overall, the `keyof` operator is a valuable tool for enhancing the type safety and maintainability of your TypeScript code, especially when working with objects and their properties.

2-8: Asynchronous typescript & promise

```ts
{
  // promise

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

  const getTodo = async (): Promise<Todo> => {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/todos/1"
    );

    const data = await response.json();
    return data;
    // console.log(data);
  };

  getTodo();

  type Something = { something: string };

  // simulate
  const createPromise = (): Promise<Something> => {
    return new Promise<Something>((resolve, reject) => {
      const data: Something = { something: "something" };
      if (data) {
        resolve(data);
      } else {
        reject("failed to load data");
      }
    });
  };

  // calling create promise function
  const showData = async (): Promise<Something> => {
    const data: Something = await createPromise();
    return data;
    // console.log(data);
  };

  showData();

  //
}
```

## 2-9: Conditional types

```ts
{
  //conditional type

  type a1 = number;
  type b1 = string;

  type x = a1 extends null ? true : false; // conditional type

  type y = a1 extends null ? true : b1 extends undefined ? undefined : any;

  type Sheikh = {
    bike: string;
    car: string;
    ship: string;
    plane: string;
  };

  //keyof Sheikh   "bike" | "car" | "ship"

  // car ase kina / bike ase kina / ship kina / tractor ase kina
  type CheckVehicle<T> = T extends keyof Sheikh ? true : false;

  type HasPlane = CheckVehicle<"plane">;
  //
}
```

## 2-10: Mapped types

```ts
  type AreaString<T> = {
    [key in keyof T]: T[key];
  };
```

```ts
{
  // mapped types

  const arrOfNumbers: number[] = [1, 4, 5];

  // const arrOfStrings : string[] = ['1','4','5']
  const arrOfStrings: string[] = arrOfNumbers.map((number) =>
    number.toString()
  );
  console.log(arrOfStrings);

  type AreaNumber = {
    height: number;
    width: number;
  };

  type Height = AreaNumber["height"]; // look up type
  // type AreaString = {
  //   height: string;
  //   width: string
  // }
  // keyof AreaNumber => "height"|"width"

  // T => {height:string;width:number}
  // key => T["width"]
  type AreaString<T> = {
    [key in keyof T]: T[key];
  };

  const area1: AreaString<{ height: string; width: number }> = {
    height: "100",
    width: 50,
  };

  //
}
```

```ts
  // T => {height:string;width:number}
  // key => T["width"]
  type AreaString<T> = {
    [key in keyof T]: T[key];
  };

  const area1: AreaString<{ height: string; width: number }> = {
    height: "100",
    width: 50,
  };
```

## 2-11 Utility types
Certainly, let's go through some examples of TypeScript utility types:

1. **`Partial<T>`**: Makes all properties of a type `T` optional.

Certainly, here's a more detailed example of how to use the `Partial<T>` utility type in TypeScript:

```typescript
// Define a type for a user with multiple properties
type User = {
  id: number;
  username: string;
  email: string;
  age: number;
};

// Create an object of the User type
const completeUser: User = {
  id: 1,
  username: "john_doe",
  email: "john@example.com",
  age: 30,
};

// Use the Partial utility type to make all properties optional
type PartialUser = Partial<User>;

// Create an object of the PartialUser type
const partialUser: PartialUser = {};

// Assign values to the optional properties
partialUser.id = 2; // Valid
partialUser.username = "jane_doe"; // Valid

// Attempting to add properties not present in the original User type is also valid
partialUser.country = "USA"; // Valid

// You can also create partial users with some properties while leaving others undefined
const partialUserWithAge: PartialUser = { age: 25 };

// Merge a partial user with a complete user
const mergedUser: User = { ...completeUser, ...partialUserWithAge };

console.log(mergedUser);
```

In this example:

- We start with a `User` type representing a user object with properties like `id`, `username`, `email`, and `age`.

- We create a complete user object `completeUser` with all properties defined.

- We use the `Partial<User>` utility type to create a new type `PartialUser` where all properties of the `User` type are optional.

- We create a partial user object `partialUser` of type `PartialUser` with no properties defined initially.

- We assign values to some optional properties of `partialUser`, demonstrating that these properties can be assigned as needed.

- We also show that it's valid to add properties to a partial user that were not present in the original `User` type, such as the `country` property.

- We create another partial user `partialUserWithAge` with the `age` property defined.

- Finally, we merge a complete user (`completeUser`) with a partial user with an `age` property (`partialUserWithAge`) to create a new user object (`mergedUser`). This showcases how you can combine complete and partial objects to get a merged result.

2. **`Required<T>`**: Makes all properties of a type `T` required.
Certainly, here's an example of the `Required<T>` utility type in TypeScript:

```typescript
// Define a type for a User with optional properties
type User = {
  id?: number;
  username?: string;
  email?: string;
  age?: number;
};

// Use the Required utility type to make all properties required
type RequiredUser = Required<User>;

// Create an object of type RequiredUser
const user: RequiredUser = {
  id: 1,
  username: "john_doe",
  email: "john@example.com",
  age: 30,
};

// Attempting to create an object with missing properties results in a type error
// const incompleteUser: RequiredUser = {
//   id: 2,
//   username: "jane_doe",
//   age: 28,
// };
```

In this example:

- We have a type `User` with optional properties, indicated by the `?` after each property name.

- We use the `Required<T>` utility type to create a new type `RequiredUser` where all properties are required.

- We create an object `user` of type `RequiredUser` with all properties present, which is valid.

- Attempting to create an object `incompleteUser` of type `RequiredUser` with missing properties results in a TypeScript type error, enforcing that all properties are required for objects of this type.

3. **`Readonly<T>`**: Makes all properties of a type `T` read-only.

Certainly, here's an example of the `Readonly<T>` utility type in TypeScript:

```typescript
type Person = {
  name: string;
  age: number;
};

type ReadonlyPerson = Readonly<Person>;

const person: ReadonlyPerson = {
  name: "Alice",
  age: 30,
};

// Attempting to modify properties will result in type errors
person.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.
person.age = 35; // Error: Cannot assign to 'age' because it is a read-only property.
```

In this example:

- We have a type `Person` representing a person with properties `name` and `age`.

- We use the `Readonly<T>` utility type to create a new type `ReadonlyPerson`, which makes all properties of `Person` read-only.

- We create an object `person` of type `ReadonlyPerson`. Attempting to modify any of its properties, such as `name` and `age`, results in TypeScript type errors. This ensures that the object remains immutable and read-only once it's defined.

4. **`Record<K, T>`**: Creates an object type with keys of type `K` and values of type `T`.

Certainly, here's an example of how to use the `Record<K, T>` utility type in TypeScript:

```typescript
// Define a type for a shopping cart item with a product ID and quantity
type CartItem = {
  productId: number;
  quantity: number;
};

// Create a type using Record<K, T> to represent a shopping cart with product IDs as keys and CartItem objects as values
type ShoppingCart = Record<string, CartItem>;

// Create a shopping cart with some items
const shoppingCart: ShoppingCart = {
  "product-1": { productId: 1, quantity: 3 },
  "product-2": { productId: 2, quantity: 2 },
};

// Add a new item to the shopping cart
shoppingCart["product-3"] = { productId: 3, quantity: 1 };

// Access and update items in the shopping cart
const item1 = shoppingCart["product-1"];
item1.quantity = 4;

// Attempting to add an item with an incorrect key type results in a type error
// shoppingCart[123] = { productId: 4, quantity: 2 }; // Error: Property '123' does not exist on type 'ShoppingCart'.
```

In this example:

- We define a type `CartItem` to represent an item in a shopping cart, which includes a `productId` and a `quantity`.

- We use the `Record<K, T>` utility type to create a type `ShoppingCart`, which represents a shopping cart with keys of type `string` (product IDs) and values of type `CartItem`.

- We create a `shoppingCart` object with some items, where each item is associated with a product ID (string key) and a `CartItem` value.

- We can add new items, access existing items, and update the quantities for items in the shopping cart.

- Attempting to add an item with an incorrect key type (e.g., a number) results in a TypeScript type error, ensuring that only valid keys are allowed in the shopping cart.

5. **`Pick<T, K>`**: Selects specific properties from a type `T`.

Certainly! Here's an example of how to use the `Pick<T, K>` utility type in TypeScript:

```typescript
// Define a type for a User with various properties
type User = {
  id: number;
  username: string;
  email: string;
  age: number;
};

// Use the Pick utility type to select specific properties from the User type
type UserIdentity = Pick<User, "id" | "username">;

// Create an object of type UserIdentity
const user: UserIdentity = {
  id: 1,
  username: "john_doe",
};

// Attempting to add properties other than "id" and "username" will result in a type error
// user.email = "john@example.com"; // Error: Property 'email' does not exist on type 'UserIdentity'.
// user.age = 30; // Error: Property 'age' does not exist on type 'UserIdentity'.
```

In this example:

- We have a type `User` representing a user object with properties like `id`, `username`, `email`, and `age`.

- We use the `Pick` utility type to create a new type `UserIdentity` by selecting specific properties `"id"` and `"username"` from the `User` type.

- We then create an object `user` of type `UserIdentity`, which only allows properties `"id"` and `"username"`. Attempting to assign or access properties other than the selected ones will result in TypeScript type errors, ensuring type safety.

6. **`Omit<T, K>`**: Excludes specific properties from a type `T`.

Certainly, here's an example of how to use the `Omit` utility type in TypeScript:

```typescript
// Define a type for a User with various properties
type User = {
  id: number;
  username: string;
  email: string;
  age: number;
};

// Use the Omit utility type to create a new type excluding the "email" property
type UserWithoutEmail = Omit<User, "email">;

// Create an object of type UserWithoutEmail
const user: UserWithoutEmail = {
  id: 1,
  username: "john_doe",
  age: 30,
};

// Attempting to add the "email" property will result in a type error
// user.email = "john@example.com"; // Error: Property 'email' does not exist on type 'UserWithoutEmail'.
```

In this example:

- We have a type `User` representing a user object with properties like `id`, `username`, `email`, and `age`.

- We use the `Omit` utility type to create a new type `UserWithoutEmail` by excluding the property `"email"` from the `User` type.

- We then create an object `user` of type `UserWithoutEmail`, which does not allow the `"email"` property. Attempting to assign the `"email"` property to this object will result in a TypeScript type error, ensuring that the property is excluded from the type.

7. **`Exclude<T, U>`**: Excludes values in type `U` from type `T`.

Certainly! Here's an example of how to use the `Exclude<T, U>` utility type in TypeScript:

```typescript
type AllColors = "Red" | "Green" | "Blue" | "Yellow";
type PrimaryColors = "Red" | "Blue";

type SecondaryColors = Exclude<AllColors, PrimaryColors>;

const secondaryColor: SecondaryColors = "Green"; // Valid
const invalidColor: SecondaryColors = "Red"; // Error: Type '"Red"' is not assignable to type 'SecondaryColors'.
```

In this example:

- We have a union type `AllColors` that represents all possible colors.

- We also have a union type `PrimaryColors` that represents primary colors.

- We use the `Exclude<AllColors, PrimaryColors>` utility type to create a new type `SecondaryColors` that excludes values from `AllColors` that are assignable to `PrimaryColors`. This effectively gives us the secondary colors.

- We assign the value `"Green"` to `secondaryColor`, which is valid because it's a secondary color.

- Attempting to assign the value `"Red"` to `invalidColor` results in a TypeScript error because `"Red"` is a primary color and cannot be a secondary color.

The `Exclude<T, U>` utility type is useful for creating new types that exclude specific values from a given union type, allowing you to enforce type safety in your code.

8. **`Extract<T, U>`**: Extracts values in type `U` from type `T`.

Certainly, here's an example of how to use the `Extract` utility type in TypeScript:

```typescript
// Define a type for different shapes
type Shape = "circle" | "square" | "triangle";

// Define a type for filled shapes
type FilledShape = "circle" | "square";

// Use the Extract utility type to extract common shapes
type CommonShape = Extract<Shape, FilledShape>;

// Create a variable of type CommonShape
const shape: CommonShape = "square"; // Valid

// Attempting to assign a non-matching shape will result in a type error
// const invalidShape: CommonShape = "triangle"; // Error: Type '"triangle"' is not assignable to type 'CommonShape'.
```

In this example:

- We have a type `Shape` representing different shapes: "circle," "square," and "triangle."

- We have a type `FilledShape` representing filled shapes: "circle" and "square."

- We use the `Extract` utility type to create a new type `CommonShape` that extracts shapes common to both `Shape` and `FilledShape`.

- We create a variable `shape` of type `CommonShape` and assign it a value that matches the common shapes, which is "square."

- Attempting to assign a shape that is not common to both `Shape` and `FilledShape`, such as "triangle," will result in a TypeScript type error, ensuring type safety.

9. **`NonNullable<T>`**: Excludes `null` and `undefined` from type `T`.

Certainly! Here's an example of how to use the `NonNullable<T>` utility type in TypeScript:

```typescript
// Define a type that includes null and undefined
type NullableString = string | null | undefined;

// Use NonNullable to exclude null and undefined from the type
type NonNullString = NonNullable<NullableString>;

// Create variables with the NonNullString type
const validString: NonNullString = "Hello, TypeScript!";
const nullValue: NonNullString = null; // Error: Type 'null' is not assignable to type 'string'.
const undefinedValue: NonNullString = undefined; // Error: Type 'undefined' is not assignable to type 'string'.
```

In this example:

- We start with a type `NullableString` that includes `string`, `null`, and `undefined`, which means it can hold either a valid string or null or undefined.

- We use the `NonNullable<NullableString>` utility type to create a new type `NonNullString`, which excludes `null` and `undefined` from the original type, ensuring that the variable can only hold valid string values.

- We create variables with the `NonNullString` type and demonstrate that attempting to assign `null` or `undefined` to them results in TypeScript type errors, ensuring that only valid strings can be assigned to these variables.

10. **`ReturnType<T>`**: Obtains the return type of a function type `T`.

Certainly! Here's an example of how to use the `ReturnType<T>` utility type in TypeScript:

```typescript
// Define a function that returns a string
function greet(name: string): string {
  return `Hello, ${name}!`;
}

// Use the ReturnType utility type to capture the return type of the function
type Greeting = ReturnType<typeof greet>;

// Create a variable of the captured return type
const message: Greeting = "Hello, Alice!";

// Attempting to assign a value of a different type will result in a type error
// const invalidMessage: Greeting = 42; // Error: Type '42' is not assignable to type 'string'.
```

In this example:

1. We define a function `greet` that takes a `name` parameter and returns a string greeting.

2. We use the `ReturnType<typeof greet>` utility type to capture the return type of the `greet` function, which is `string`.

3. We create a variable `message` of type `Greeting`, which is equivalent to `string`.

4. Attempting to assign a value of a different type, such as a number, to `Greeting` will result in a TypeScript type error, demonstrating the utility of `ReturnType` for ensuring the correct return type of functions.

11. **`Parameters<T>`**: Obtains the parameter types of a function type `T` as a tuple.

Certainly, here's an example of how to use the `Parameters<T>` utility type in TypeScript:

```typescript
// Define a function type with parameters
type AddFunction = (x: number, y: number) => number;

// Use the Parameters utility type to extract parameter types
type AddFunctionArgs = Parameters<AddFunction>;

// Create a function that uses the extracted parameter types
const add: AddFunction = (a, b) => a + b;

// Valid usage with correct parameter types
const result: number = add(3, 4);

// Invalid usage with incorrect parameter types
const invalidResult: number = add("3", 4); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
```

In this example:

- We define a function type `AddFunction` that represents a function with two parameters of type `number` and a return type of `number`.

- We use the `Parameters` utility type to extract the parameter types from the `AddFunction` type. This results in the `AddFunctionArgs` type, which is a tuple of the parameter types.

- We create a function `add` of type `AddFunction` that takes two parameters, `a` and `b`, and returns their sum.

- We use the `add` function with valid parameter types (`number`) to compute the sum, which is `7`.

- We also attempt to use the `add` function with invalid parameter types (`string` and `number`), which results in a TypeScript error due to type incompatibility.

This example demonstrates how the `Parameters<T>` utility type can be used to extract parameter types from a function type and ensure type safety when working with functions.

12. **`InstanceType<T>`**: Obtains the instance type of a constructor function type `T`.

Certainly, here's an example of how to use the `InstanceType<T>` utility type in TypeScript:

```typescript
// Define a class with a constructor
class Product {
  constructor(public name: string, public price: number) {}
}

// Use the InstanceType utility type to obtain the instance type of the Product class
type ProductInstance = InstanceType<typeof Product>;

// Create an instance of the Product class
const product: ProductInstance = new Product("Laptop", 999);

// Access properties and methods of the instance
console.log(`Product: ${product.name}, Price: $${product.price}`);
```

In this example:

- We define a `Product` class with a constructor that takes a `name` and a `price`.

- We use the `InstanceType<typeof Product>` utility type to obtain the instance type of the `Product` class. This allows us to create instances with the same shape as `Product`.

- We create an instance `product` of type `ProductInstance`, passing the required constructor parameters.

- We can access the properties and methods of the `product` instance, ensuring type safety and code clarity.

**==========================================================================================================================**

**# 3. Object-Oriented Programming (OOP) with TypeScript
**
## 3-0: Introduction of Object Oriented Programming

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nxveg9nilsqfyw5l01ug.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tyordb86wd0mmwapp2zc.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ppdgwpulkedshito89z.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n1ycgxaozaio2spjy96d.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y34pcvtyg9k4i9mcb0q2.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/od76b834wkhb7omat29i.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gf8e4g2ngsx54bhz4om1.png)

## 3-1: Class and object

```ts
{
  // oop - class

  class Animal {
    constructor(
      public name: string,
      public species: string,
      public sound: string
    ) {}

    makeSound() {
      console.log(`The ${this.name} says ${this.sound}`);
    }
  }

  const dog = new Animal("German Shepard Bhai", "dog", "Ghew Ghew");
  const cat = new Animal("Persian bhai", "cat", "meaw meaw");

  cat.makeSound();

  //
}
```

## 3-2: Inheritance in OOP.

```ts
{
  // oop - inheritence
  class Person {
    name: string;
    age: number;
    address: string;

    constructor(name: string, age: number, address: string) {
      this.name = name;
      this.age = age;
      this.address = address;
    }
    getSleep(numOfHours: number) {
      console.log(`${this.name} will sleep for ${numOfHours}`);
    }
  }


  class Student extends Person {
    constructor(name: string, age: number, address: string) {
      super(name, age, address)
    }
   
  }

  const student1 = new Student("Mr. student", 20, "Uganda");
  student1.


  class Teacher extends Person{
    designation: string

    constructor(name: string, age: number, address: string,designation: string) {
      super(name, age , address)
      this.designation = designation
    }
   
   takeClass(numOfClass: number){
    console.log(`${this.name} will take ${numOfClass}`);
   }
  }

  const teacher = new Teacher("Mr. teacher", 40, "Uganda","professor");
  teacher.
  //
} 
```

## 3-3: Type guard using typeof & in

```ts
"role" in user
typeof param1 === "number"
```

```ts
{
  // type guards

  // typeof --> type guard

  type Alphaneumeric = string | number;

  const add = (param1: Alphaneumeric, param2: Alphaneumeric): Alphaneumeric => {
    if (typeof param1 === "number" && typeof param2 === "number") {
      return param1 + param2;
    } else {
      return param1.toString() + param2.toString();
    }
  };

  const result1 = add("2", "3");
  console.log(result1);

  // in guard
  type NormalUser = {
    name: string;
  };

  type AdminUser = {
    name: string;
    role: "admin";
  };

  const getUser = (user: NormalUser | AdminUser) => {
    if ("role" in user) {
      console.log(`My name is ${user.name} and my role is ${user.role}`);
    } else {
      console.log(`My name is ${user.name}`);
    }
  };

  const normalUser: NormalUser = {
    name: "Mr. Normal Bhai",
  };
  const adminUser: AdminUser = {
    name: "Mr. Admin Bhai",
    role: "admin",
  };

  getUser(adminUser);

  //
}
```

## 3-4: Type guard using instance of

```ts
animal instanceof Dog;
animal instanceof Cat;
```

```ts
  const isDog = (animal: Animal): animal is Dog => {
    return animal instanceof Dog;
  };
```

```ts
{
  // instanceof guard
  class Animal {
    name: string;
    species: string;

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

    makeSound() {
      console.log("I am making sound");
    }
  }

  class Dog extends Animal {
    constructor(name: string, species: string) {
      super(name, species);
    }
    makeBark() {
      console.log("I am barking");
    }
  }

  class Cat extends Animal {
    constructor(name: string, species: string) {
      super(name, species);
    }
    makeMeaw() {
      console.log("I am mewaing");
    }
  }

  // smart way tge handle korar jnne chaile amra function bebohar krte pari
  const isDog = (animal: Animal): animal is Dog => {
    return animal instanceof Dog;
  };

  const isCat = (animal: Animal): animal is Cat => {
    return animal instanceof Cat;
  };

  const getAnimal = (animal: Animal) => {
    if (isDog(animal)) {
      animal.makeBark();
    } else if (isCat(animal)) {
      animal.makeMeaw();
    } else {
      animal.makeSound();
    }
  };

  const dog = new Dog("Dog Bhai", "dog");
  const cat = new Cat("Cat Bhai", "cat");

  getAnimal(cat);

  //
}
```

## 3-5: Access modifiers

```ts
    public readonly id: number;
    public name: string;
    private _balance: number;
```

```ts
this._balance = balance;
```

```ts
{
  // access modifiers
  class BankAccount {
    public readonly id: number;
    public name: string;
    private _balance: number;

    constructor(id: number, name: string, balance: number) {
      this.id = id;
      this.name = name;
      this._balance = balance;
    }

   public  addDeposit(amount: number) {
      this._balance = this._balance + amount;
    }

   public getBalance() {
      return this._balance;
    }
  }

  class StudentAccount extends BankAccount{
    test(){
      this.
    }
  }

  const goribManusherAccount = new BankAccount(111, "Mr. gorib", 20);
  // goribManusherAccount.balance = 0;
  goribManusherAccount.addDeposit(20);
  const myBalance = goribManusherAccount.getBalance();
  console.log(myBalance);

  //
}
```

## 3-6: Getter and setter

```ts
    set deposit(amount: number) {
      this._balance = this.balance + amount;
    }
```

```ts
    get balance() {
      return this._balance;
    }
```

```ts
{
  // getter and setter
  class BankAccount {
    public readonly id: number;
    public name: string;
    protected _balance: number;

    constructor(id: number, name: string, balance: number) {
      this.id = id;
      this.name = name;
      this._balance = balance;
    }

    set deposit(amount: number) {
      this._balance = this.balance + amount;
    }
    // public addDeposit(amount: number) {
    //   this._balance = this._balance + amount;
    // }

    //getter
    get balance() {
      return this._balance;
    }
    // public getBalance() {
    //   return this._balance;
    // }
  }

  const goribManusherAccount = new BankAccount(111, "Mr. gorib", 50);

  // goribManusherAccount.deposit = 0;
  // goribManusherAccount.addDeposit(20); // function call korte hsse
  goribManusherAccount.deposit = 50;
  // const myBalance = goribManusherAccount.getBalance(); // function call korte hsse

  const myBalance = goribManusherAccount.balance; // property er mto kore
  console.log(myBalance);

  //
}
```

## 3-7 Statics in OOP

```ts
=> if use static in anyy variable then have to use classname dot variable name to access.
static count: number = 0;
return (Counter.count = Counter.count + 1);
```

```ts
=>if use static in the function then during the log we have to use classname dot function.
 static increment() {
}
console.log(Counter.increment());
```


```ts
{
  // static
  class Counter {
    static count: number = 0;

    static increment() {
      return (Counter.count = Counter.count + 1);
    }

    static decrement() {
      return (Counter.count = Counter.count - 1);
    }
  }

  // const instance1 = new Counter();
  console.log(Counter.increment()); // 1 -> different memory
  // 1 -> different memory

  // const instance2 = new Counter();
  console.log(Counter.increment()); // 1  --> different memory
  // 1  --> different memory

  // const instance3 = new Counter();
  console.log(Counter.increment());
  //
}
```

## 3-8 Polymorphism

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ziy3ix7osw7he6emnfqr.png)

```ts
{
  // polymorphisom

  class Person {
    getSleep() {
      console.log(`I am sleeping for 8 hours per day`);
    }
  }

  class Student extends Person {
    getSleep() {
      console.log(`I am sleeping for 7 hours per day`);
    }
  }

  class Developer extends Person {
    getSleep() {
      console.log(`I am sleeping for 6 hours per day`);
    }
  }

  const getSleepingHours = (param: Person) => {
    param.getSleep();
  };

  const person1 = new Person();
  const person2 = new Student();
  const person3 = new Developer();

  getSleepingHours(person1);
  getSleepingHours(person2);
  getSleepingHours(person3);

  class Shape {
    getArea(): number {
      return 0;
    }
  }

  // pi* r* r
  class Circle extends Shape {
    radius: number;
    constructor(radius: number) {
      super();
      this.radius = radius;
    }

    getArea(): number {
      return Math.PI * this.radius * this.radius;
    }
  }

  // height * width
  class Reactangle extends Shape {
    height: number;
    width: number;

    constructor(height: number, width: number) {
      super();
      this.height = height;
      this.width = width;
    }

    getArea(): number {
      return this.height * this.width;
    }
  }

  const getShapeArea = (param: Shape) => {
    console.log(param.getArea());
  };

  const shape1 = new Shape();
  const shape2 = new Circle(10);
  const shape3 = new Reactangle(10, 20);

  getShapeArea(shape3);

  //
}
```


## 3-9 Abstraction in OOP

```ts
`interface Vehicle1 `
`class Car1 implements Vehicle1`
`abstract class Car2`
`class ToyotaCar extends Car2 `
```
`  abstract class Car2 {
    abstract startEngine(): void;
    abstract stopEngine(): void;
    abstract move(): void;
    test() {
      console.log(`I am just testing`);
    }
  }`

```ts
{
  // abstraction  : 1. interface 2. abstract

  // idea
  interface Vehicle1 {
    startEngine(): void;
    stopEngine(): void;
    move(): void;
  }

  // real implementation
  class Car1 implements Vehicle1 {
    startEngine(): void {
      console.log(`I am starting the car engine`);
    }
    stopEngine(): void {
      console.log("I am stopping the car engine");
    }
    move(): void {
      console.log(`I am moving the car`);
    }
    test() {
      console.log(`I am just testing`);
    }
  }

  const toyotaCar = new Car1();
  toyotaCar.startEngine();

  // abstract class

  // idea
  abstract class Car2 {
    abstract startEngine(): void;
    abstract stopEngine(): void;
    abstract move(): void;
    test() {
      console.log(`I am just testing`);
    }
  }

  class ToyotaCar extends Car2 {
    startEngine(): void {
      console.log("I am starting the car  engine");
    }
    stopEngine(): void {
      console.log("I am stopping the car engine");
    }
    move(): void {
      console.log("I am moving  the car");
    }
  }

  // const hondaCar = new Car2();
  // hondaCar.startEngine();

  //
}
```

## 3-10: Encapsulation in OOP

```ts
    public readonly id: number;
    public name: string;
    protected _balance: number;
   private getBalance() {
      return this._balance;
    }
```

```ts
    getHiddenMethod(){
      return this.getBalance()
    }

---
  class StudentAccount extends BankAccount{
    test(){
      this.
    }

```

```ts
{
  // access modifiers
  class BankAccount {
    public readonly id: number;
    public name: string;
    protected _balance: number;

    constructor(id: number, name: string, balance: number) {
      this.id = id;
      this.name = name;
      this._balance = balance;
    }

   public  addDeposit(amount: number) {
      this._balance = this._balance + amount;
    }

   private getBalance() {
      return this._balance;
    }

    getHiddenMethod(){
      return this.getBalance()
    }
  }

  class StudentAccount extends BankAccount{
    test(){
      this.
    }
  }

  const goribManusherAccount = new BankAccount(111, "Mr. gorib", 20);
  // goribManusherAccount.balance = 0;
  goribManusherAccount.addDeposit(20);
  const myBalance = goribManusherAccount.getBalance();
  console.log(myBalance);

  goribManusherAccount.

  //
}
```