0% found this document useful (0 votes)
6 views69 pages

Type Script

The document explains various TypeScript features including utility types like Readonly, Required, and Partial, as well as generics, enums, and their functionalities. It covers how to create and use enums, their types (numeric, string, heterogeneous), and concepts like reverse mapping and computed members. Additionally, it discusses the advantages of using generics for type safety and flexibility in function definitions.

Uploaded by

2101020064
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as ODT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views69 pages

Type Script

The document explains various TypeScript features including utility types like Readonly, Required, and Partial, as well as generics, enums, and their functionalities. It covers how to create and use enums, their types (numeric, string, heterogeneous), and concepts like reverse mapping and computed members. Additionally, it discusses the advantages of using generics for type safety and flexibility in function definitions.

Uploaded by

2101020064
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as ODT, PDF, TXT or read online on Scribd
You are on page 1/ 69

TS:

Readonly/readonly:

 readonly (lowercase) is a modifier for individual properties.


 Readonly (uppercase) is a utility type for making all properties or
elements immutable.

Required:

The Required<T> utility type is the opposite of Partial<T>. It


takes a type T and makes all of its properties required

For example,

interface User {

id?: number;

name?: string;

type RequiredUser = Required<User>;

// const user2: RequiredUser = { id: 2 }; // Error: Property 'name'


is missing

Partial<T>:

Partial<T> utility type in TypeScript is used to create a new type


where all properties of the original type T are optional.

For eg :

interface Person {

name: string;

age: number;

address: string;

If you want to create a type where all properties of Person are optional,
you can use Partial<Person>:

type PartialPerson = Partial<Person>;

const person: PartialPerson = { name: "Alice" }; // Valid


const person2: PartialPerson = { age: 25 }; // Valid

const person3: PartialPerson = {}; // Valid

Pick:

Omit:

Type vs Interface :

Generic :
function identity ( arg : any):any{

return arg;

Problem with any is that suppose we pass no in arg but we can


also get string

If passed any if we want to keep the info about type that is if arg
is no I want no then I will use generics

For example,

function identity <T> ( arg : T):T{

return arg;

During this function identity call is in place of t something is passed

For eg :
let output = identity<string>("myString");

Here T becomes string


But if it is like

let output = identity ("myString");

it will automatically infer T as string

Here the <T> we can pass string,no anything (interface and types also )

Example 1 for types :

type Person = {

name: string;

age: number;

};

function identity<T>(arg: T): T {

return arg;

Can also pass multiple alias type

Function identity1<T,U>(arg1:T,arg2:U):U{

return arg2;

const person: Person = { name: "Alice", age: 25 };

const result = identity<Person>(person);

console.log(result);

Example 2 for interface :

interface Person {

name: string;

age: number;

}
function identity<T>(arg: T): T {

return arg;

const person: Person = { name: "Alice", age: 25 };

const result = identity<Person>(person);

console.log(result);

geneic interface :

interface KeyPair<T,U> { //here KeyPair is the name we have given to


this interface

key:T,

value:U; // both , and ; after will work

Let xyz :KeyPair<string ,number> ={

Key:”pair”,

Value: 20;

};

Generic constraints:

function getlength <T extends {length:number}>(arg:T):number{

return arg.length;

Here the extends {length :number } makes sure that the arg which we
are passing should have a length property so if in this function getlen gth
I am passing a number it will have an error as type no has no property
like length but if arg is array or string it works as they have length
property

Example :
function foo1 <T extends {length:number}> (arg:T){

return arg.length;

const prop={name:"ashim",length:23};

const arr=[1,2,3];

const str="helllo";

console.log(foo1(arr));

console.log(foo1(str));

console.log(foo1(prop));

Q. How to pass multiple property like length and


suppose any method like push or some other property in
generic constraints ?

Soln :

function foo1 <T extends {length:number, age:number}>


(arg:T){
return (`length is ${arg.length} + age is ${arg.age}`);

}
const prop={name:"ashim",length:23,age:3};
console.log(foo1(prop));

an interesting question:??
function foo <T="string">(agr1:number,arg2:T):T{ // if no value is
given to T then string will be default

return arg2;

console.log(foo(2,"this"));

console.log(foo(2,2)) // it will give 2 as by looking at parameter it will


infer it

function createArray<T = string>(length: number, value: T): T[] {

return Array(length).fill(value);

const stringArray = createArray(3, "hello"); // T defaults to string if i


change "hello" to 2

// Q:it will still work as it will implicitly take the type as number so why
default is needed ??

const numberArray = createArray<number>(3, 42); // T is explicitly


number

console.log(stringArray);

console.log(numberArray);

Generics important things :

Q. What is the purpose of using keyof with generics?

Q.What are mapped types in TypeScript, and how do they work with
generics?

Q. What is the difference between T and T & SomeType in generics?

Enums: on converting to js the official ts


compiler at the ts
Official website is changing It to let so scope will
be as let
And every thing like variable shadowing will be
applied
In case of type when you create type let const
var nothing is written in js when you create
entity of that type then let const and var are
given to it on that time only
For example :
type people={ // no let var const here

name:string,

age:number

let pep1:people={name:"ashim" ,age:23}; //here it will be let

const pep2:people={name:"ashim" ,age:23}; // here it will be const

var pep3: people={name:"can" ,age:23}; // here it will be var

For example:
enum Status {

Pending=110,

Approved,

Rejected

enum Status {

Pending,
Approved,

Rejected

console.log(Status.Pending); //0

console.log(Status.Pending); //110

Enums in TypeScript are a powerful feature that allow you to define a set of
named constants. They can be **numeric**, **string-based**, or even
**heterogeneous** (mixed). Below, I'll cover **all types of enums** with
examples to help you understand their usage and nuances.

---

the key in a TypeScript enum doesn't have to be a string—it must be a valid


identifier, which means it can only be a name that follows the naming rules for
variables (e.g., no spaces or special characters).

For example :
enum direction {

Up=1,

"down",

left,

right,

3 // ERROR HERE AS numeric name not allowed as it must be a name that


is it should be an identifier

console.log(direction.Up);

console.log(direction[3]);// "left"

console.log(direction.down);
console.log(direction[2]) // "down"

### **1. Numeric Enums**

Numeric enums are the default in TypeScript. They auto-increment their values
starting from `0` unless explicitly set.

#### Example:

```typescript

enum Direction {

Up, // 0

Down, // 1

Left, // 2

Right // 3

console.log(Direction.Up); // 0

console.log(Direction.Down); // 1

```

#### Custom Numeric Values:

You can explicitly set values for enum members.

```typescript

enum Status {

Pending = 10,

Approved = 20,

Rejected = 30

console.log(Status.Pending); // 10
console.log(Status.Approved); // 20

```

---

### **2. String Enums**

String enums use string values instead of numbers. Each member must be
initialized with a string.

#### Example:

```typescript

enum LogLevel {

Info = "INFO",

Warn = "WARN",

Error = "ERROR"

console.log(LogLevel.Info); // "INFO"

console.log(LogLevel.Error); // "ERROR"

```

#### Use Case:

String enums are useful when you need human-readable values or when working
with APIs that expect specific string values.

---

### **3. Heterogeneous Enums**

Heterogeneous enums mix numeric and string values. This is allowed in


TypeScript but is generally discouraged because it can lead to confusion.

#### Example:
```typescript

enum Response {

No = 0,

Yes = "YES"

console.log(Response.No); // 0

console.log(Response.Yes); // "YES"

```

---

### **4. Computed and Constant Members**

Enum members can have **constant** or **computed** values.

#### Constant Members:

- Values are known at compile time.

- Can be a literal (e.g., `1`, `"YES"`) or a reference to another constant enum


member.

#### Computed Members:

- Values are computed at runtime.

#### Example:

```typescript

enum FileAccess {

Read = 1 << 1, // Computed: 2

Write = 1 << 2, // Computed: 4

ReadWrite = Read | Write // Computed: 6

}
console.log(FileAccess.ReadWrite); // 6

```

---

### **5. Reverse Mapping**

Numeric enums support **reverse mapping**, meaning you can access the
enum member name from its value.

#### Example:

```typescript

enum Direction {

Up = 1,

Down,

Left,

Right

console.log(Direction.Up); // 1

console.log(Direction[1]); // "Up"

```

#### Note:

- Reverse mapping is **not supported** for string enums.

---

### **6. Const Enums**

Const enums are inlined at compile time, which can improve performance.
However, they don’t support reverse mapping.
#### Example:

```typescript

const enum Colors {

Red = "RED",

Green = "GREEN",

Blue = "BLUE"

console.log(Colors.Red); // "RED"

```

#### Use Case:

Use const enums when you want to avoid the runtime overhead of regular
enums.

---

### **7. Enums at Runtime**

Enums are real objects that exist at runtime. You can use them in conditions,
loops, and other runtime logic.

#### Example:

```typescript

enum LogLevel {

Info = "INFO",

Warn = "WARN",

Error = "ERROR"

function logMessage(level: LogLevel, message: string) {

console.log(`[${level}] ${message}`);
}

logMessage(LogLevel.Info, "This is an info message."); // [INFO] This is an info


message.

```

---

### **8. Enums with Static Methods**

You can extend enums with static methods by merging them with a namespace.

#### Example:

```typescript

enum Size {

Small = "S",

Medium = "M",

Large = "L"

namespace Size {

export function isSmall(size: Size): boolean {

return size === Size.Small;

console.log(Size.isSmall(Size.Small)); // true

console.log(Size.isSmall(Size.Large)); // false

```

---
### **9. Enums as Union Types**

Enums can be used to create union types, which are useful for type-checking.

#### Example:

```typescript

enum Shape {

Circle = "CIRCLE",

Square = "SQUARE"

type Circle = {

kind: Shape.Circle;

radius: number;

};

type Square = {

kind: Shape.Square;

sideLength: number;

};

type ShapeType = Circle | Square;

function area(shape: ShapeType): number {

switch (shape.kind) {

case Shape.Circle:

return Math.PI * shape.radius ** 2;

case Shape.Square:

return shape.sideLength ** 2;

}
const circle: Circle = { kind: Shape.Circle, radius: 5 };

console.log(area(circle)); // 78.53981633974483

```

---

### **10. Enums vs Union Types**

Sometimes, a union type is a better alternative to enums, especially when you


don’t need the runtime behavior of enums.

#### Example:

```typescript

type LogLevel = "INFO" | "WARN" | "ERROR";

function logMessage(level: LogLevel, message: string) {

console.log(`[${level}] ${message}`);

logMessage("INFO", "This is an info message."); // [INFO] This is an info message.

```

---

### **Summary of Enum Types**

| Type | Example | Use Case


|

|--------------------|-------------------------------------------|-----------------------------------------------
--------------------------|

| **Numeric Enum** | `enum Direction { Up, Down }` | Auto-


incrementing values, reverse mapping. |
| **String Enum** | `enum LogLevel { Info = "INFO" }` | Human-readable
values, API compatibility. |

| **Heterogeneous** | `enum Response { No = 0, Yes = "YES" }` | Rarely used,


mixes numeric and string values. |

| **Const Enum** | `const enum Colors { Red = "RED" }` | Inlined at


compile time, no runtime overhead. |

| **Computed** | `enum FileAccess { Read = 1 << 1 }` | Computed


values at runtime. |

| **Reverse Mapping**| `console.log(Direction[1]);` | Access enum


member name from value (numeric enums only). |

---

Enums are a versatile feature in TypeScript, but they should be used judiciously.
For simpler cases, union types might be a better choice. Let me know if you need
further clarification! 😊

Reverse Mapping in case of Enums :


Reverse mapping is a feature of **numeric enums** in TypeScript. It allows you
to retrieve the **name of an enum member** from its **value**. This is
particularly useful when you have a value and want to find out which enum
member it corresponds to.

---

### **How Reverse Mapping Works**

When you define a numeric enum, TypeScript generates a **bidirectional


mapping** at runtime. This means:

- You can access the **value** of an enum member using its **name**.

- You can also access the **name** of an enum member using its **value**.

---

### **Example of Reverse Mapping**


#### Numeric Enum:

```typescript

enum Direction {

Up = 1,

Down,

Left,

Right

```

#### Accessing Values:

```typescript

console.log(Direction.Up); // 1

console.log(Direction.Down); // 2

console.log(Direction.Left); // 3

console.log(Direction.Right); // 4

```

#### Reverse Mapping (Accessing Names):

```typescript

console.log(Direction[1]); // "Up" in string form

console.log(Direction[2]); // "Down"

console.log(Direction[3]); // "Left"

console.log(Direction[4]); // "Right"

```

---

### **How It Works Under the Hood check it once **

When you define a numeric enum like this:

```typescript
enum Direction {

Up = 1,

Down,

Left,

Right

```

TypeScript generates the following JavaScript code:

```javascript

var Direction;

(function (Direction) {

Direction[Direction["Up"] = 1] = "Up";

Direction[Direction["Down"] = 2] = "Down";

Direction[Direction["Left"] = 3] = "Left";

Direction[Direction["Right"] = 4] = "Right";

})(Direction || (Direction = {}));

```

This creates a bidirectional mapping:

```javascript

1: "Up",

2: "Down",

3: "Left",

4: "Right",

Up: 1,

Down: 2,

Left: 3,

Right: 4

```
---

### **Limitations of Reverse Mapping**

1. **Only Works with Numeric Enums:**

- Reverse mapping is **not supported** for string enums or heterogeneous


enums (enums with mixed numeric and string values).

**Example (String Enum):**

```typescript

enum LogLevel {

Info = "INFO",

Warn = "WARN",

Error = "ERROR"

console.log(LogLevel["INFO"]); // Error: No reverse mapping

```

2. **No Reverse Mapping for Computed Members:**

- If an enum member has a computed value, it won’t be included in the


reverse mapping.

**Example:**

```typescript

enum FileAccess {

Read = 1 << 1, // Computed: 2

Write = 1 << 2 // Computed: 4

console.log(FileAccess[2]); // "Read"

console.log(FileAccess[4]); // "Write"

console.log(FileAccess[6]); // undefined (no reverse mapping for computed


combinations)
```

---

### **Practical Use Cases for Reverse Mapping**

1. **Debugging:**

- When you have a numeric value and want to know which enum member it
represents.

**Example:**

```typescript

enum Status {

Pending = 0,

Approved = 1,

Rejected = 2

const statusValue = 1;

console.log(Status[statusValue]); // "Approved"

```

2. **Dynamic Lookup:**

- When you need to dynamically look up enum members based on runtime


values.

**Example:**

```typescript

enum Color {

Red = 1,

Green = 2,

Blue = 3

}
function getColorName(value: number): string {

return Color[value] || "Unknown";

console.log(getColorName(2)); // "Green"

console.log(getColorName(5)); // "Unknown"

```

3. **Serialization/Deserialization:**

- When converting between numeric values and their corresponding enum


names (e.g., in APIs or databases).

**Example:**

```typescript

enum Role {

Admin = 1,

User = 2,

Guest = 3

// Serialize

const roleValue = Role.Admin; // 1

// Deserialize

const roleName = Role[roleValue]; // "Admin"

console.log(roleName);

```

---

### **Reverse Mapping with Custom Values**


If you explicitly set values for enum members, reverse mapping still works as
long as the values are unique.

#### Example:

```typescript

enum HttpStatus {

OK = 200,

BadRequest = 400,

NotFound = 404,

InternalServerError = 500

console.log(HttpStatus[200]); // "OK"

console.log(HttpStatus[404]); // "NotFound"

```

---

### **Reverse Mapping with Non-Sequential Values**

Reverse mapping works even if the enum values are not sequential.

#### Example:

```typescript

enum Priority {

Low = 10,

Medium = 20,

High = 30

console.log(Priority[10]); // "Low"

console.log(Priority[30]); // "High"

```
---

### **Key Takeaways**

- Reverse mapping is a feature of **numeric enums** that allows you to


retrieve the name of an enum member from its value.

- It works because TypeScript generates a **bidirectional mapping** at


runtime.

- Reverse mapping is **not supported** for string enums or computed


members.

- It’s useful for debugging, dynamic lookups, and serialization/deserialization.

Let me know if you need further clarification or more examples! 😊

Computed Enums : they get computed at the runtime


and not in the compile time …

Example:

enum Size {

Small = 10,

Medium = Small * 2,

Large = ExtraLarge + 10, //it is going to give an error // A member initializer


in a enum declaration cannot reference

//members declared after it, including members defined in other enums

ExtraLarge = Medium * 2 // yea you can use the previous values

// (not only immediate previous any previous)

Example :
function getDiscount(): number {

return 15; // Imagine this is computed dynamically

enum Price {
Base = 100,

Discounted = Base - getDiscount(), // Computed: 85

Premium = Base * 2 // Computed: 200

console.log(Price.Base); // 100

console.log(Price.Discounted); // 85

console.log(Price.Premium); // 200

console.log(Size.Small);

console.log(Size.Medium);

console.log(Size.Large);

console.log(Size.ExtraLarge);

Example :

enum Size {

Small = 10,

Medium = Small * 2,

Medium= , // not allowed as it is dulicate

medium= // it can be done as it will be treated diffrenent form that Medium

Large = ExtraLarge + 10, //it is going to give an error // A member initializer


in a enum declaration cannot reference

//members declared after it, including members defined in other enums

ExtraLarge = Medium * 2 // yea you can use the previous values

// (not only immediate previous any previous)

console.log(Size.Small); // 10

console.log(Size.Medium); // 20

console.log(Size.Large); // 30

console.log(Size.ExtraLarge); // 60
Some other cases :
enum LogLevel {

Info = "INFO",

Warn = "WARN",

Error = -20,

WATER // water will have -19 value if Error is -0 then WATER will be 1 as -0
is 0 and

// it will increment it by one

enum enum2{

UP="hello",

left

// this will show error because if you have taken one string then below that
you have to assign

// something if number you have assigned somewhere then the tings after
that will take plus value

// of the previous one if previous one is a number

console.log(LogLevel.Info); // "INFO"

console.log(LogLevel.Error); //-20

console.log(LogLevel.WATER); //(logs -19 here in this case )if Error is 20 it will


log 21

types and interface :


Sure! Let’s dive deep into **TypeScript
interfaces vs. types** and explain each
point in detail with examples.

---

### **1. Basic Syntax**

#### **Interface:**
Interfaces are used to define the shape of
an object. They are primarily used for
object-oriented programming and are
extendable.

```typescript
interface Person {
name: string;
age: number;
}
```
- Here, `Person` is an interface that
requires an object to have `name` (string)
and `age` (number) properties.

#### **Type:**
Types are more flexible and can define not
only object shapes but also primitives,
unions, and tuples.

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

- Here, `Person` is a type alias that defines


the same object shape as the `Person`
interface.

---

### **2. Extending/Inheritance**


#### **Interface:**
Interfaces can extend other interfaces or
types using the `extends` keyword. This is
useful for creating hierarchies or combining
multiple interfaces.

```typescript
interface Animal {
name: string;
}

interface Dog extends Animal {


breed: string;
}
```

- Here, `Dog` extends `Animal` and adds a


`breed` property.

#### **Type:**
Types can combine other types using
**intersection (`&`)**. This is similar to
extending interfaces but is more flexible.

```typescript
type Animal = {
name: string;
};

type Dog = Animal & {


breed: string;
};
```

- Here, `Dog` combines `Animal` with an


additional `breed` property using an
intersection.

---

### **3. Adding Properties**

#### **Interface:**
Interfaces support **declaration merging**,
meaning you can define an interface
multiple times, and TypeScript will merge
them into a single interface.

```typescript
interface Person {
name: string;
}

interface Person {
age: number;
}

const person: Person = {


name: "Alice",
age: 30
};
```

- Here, the two `Person` interfaces are


merged into one, requiring both `name`
and `age` properties.
#### **Type:**
Types cannot be reopened or merged. Once
a type is defined, it cannot be changed.

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

// Error: Duplicate identifier 'Person'


type Person = {
age: number;
};
```

- This will result in a TypeScript error


because types cannot be extended or
merged.

---
### **4. Union and Intersection Types**

#### **Interface:**
Interfaces cannot directly represent
**union** or **intersection** types.
However, they can extend union types.

```typescript
interface A {
x: number;
}

interface B {
y: string;
}

type C = A & B; // Intersection


type D = A | B; // Union
```

- Here, `C` is an intersection of `A` and `B`,


and `D` is a union of `A` and `B`.
#### **Type:**
Types can directly represent **union** and
**intersection** types.

```typescript
type A = { x: number };
type B = { y: string };

type C = A & B; // Intersection


type D = A | B; // Union
```

- Here, `C` is an intersection of `A` and `B`,


and `D` is a union of `A` and `B`.

---

### **5. Primitives, Unions, and Tuples**

#### **Interface:**
Interfaces cannot define **primitives**,
**unions**, or **tuples**.

```typescript
// Error: Interfaces cannot define primitives
or unions
interface StringOrNumber = string |
number;
```

- This will result in a TypeScript error


because interfaces are only for object
shapes.

#### **Type:**
Types can define **primitives**, **unions**,
and **tuples**.

```typescript
type StringOrNumber = string | number; //
Union
type Point = [number, number]; // Tuple
```
- Here, `StringOrNumber` is a union of
`string` and `number`, and `Point` is a
tuple representing a 2D point.

---

### **6. Implementing in Classes**

#### **Interface:**
Interfaces can be **implemented** by
classes. This is useful for enforcing a
contract (shape) on a class.

```typescript
interface Animal {
name: string;
makeSound(): void;
}

class Dog implements Animal {


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

makeSound() {
console.log("Woof!");
}
}
```

- Here, the `Dog` class implements the


`Animal` interface, ensuring it has `name`
and `makeSound` properties.

#### **Type:**
Types cannot be directly implemented by
classes. However, you can achieve similar
behavior using intersection types.

```typescript
type Animal = {
name: string;
makeSound(): void;
};

class Dog implements Animal {


name: string;

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

makeSound() {
console.log("Woof!");
}
}
```

- Here, the `Dog` class implements the


`Animal` type, which behaves similarly to
an interface.

---

### **7. Performance**


- **Interfaces** are generally faster for
TypeScript to check during compilation
because they are designed specifically for
object shapes.
- **Types** can be more complex (e.g.,
unions, intersections), which can slow down
type checking.

---

### **8. Use Cases**

#### **Use Interfaces When:**


- You need to define the shape of an object.
- You want to use **declaration merging**
to extend an existing interface.
- You are working with object-oriented
programming (e.g., classes implementing
interfaces).

#### **Use Types When:**


- You need to define **union types**,
**intersection types**, or **tuples**.
- You are working with complex types that
cannot be expressed with interfaces.
- You want to define aliases for primitives
or other types.

---

### **9. Compatibility**

- Interfaces and types are mostly


compatible with each other. For example,
you can use an interface where a type is
expected, and vice versa.

```typescript
interface A {
x: number;
}

type B = {
y: string;
};
type C = A & B; // Combining interface and
type
```

- Here, `C` is an intersection of `A`


(interface) and `B` (type).

---

### **10. Example: When to Use Which**

#### **Interface Example:**


```typescript
interface User {
id: number;
name: string;
}

interface Admin extends User {


role: string;
}
const admin: Admin = {
id: 1,
name: "Alice",
role: "Admin"
};
```

- Here, `Admin` extends `User` and adds a


`role` property.

#### **Type Example:**


```typescript
type ID = number | string;

type Point = {
x: number;
y: number;
};

type Shape = Point | { radius: number }; //


Union type
const shape: Shape = { x: 10, y: 20 };
```

- Here, `Shape` is a union of `Point` and an


object with a `radius` property.

---

### **Summary of Differences**

| Feature | **Interface**
| **Type** |
|--------------------------|--------------------------------
--------|---------------------------------------|
| **Extending** | `extends` keyword
| Intersection (`&`) |
| **Adding Properties** | Supports
declaration merging | Cannot be
reopened |
| **Unions/Intersections** | Cannot define
directly | Can define directly
|
| **Primitives/Tuples** | Cannot define
| Can define |
| **Class Implementation** | Can be
implemented by classes | Cannot be
directly implemented |
| **Use Cases** | Object shapes, OOP
| Complex types, unions, intersections |

---

### **When to Use Which?**

- Use **interfaces** for defining object


shapes and when you need declaration
merging.
- Use **types** for defining unions,
intersections, tuples, or aliases for
primitives.

Both are powerful tools, and the choice


often comes down to personal preference
or specific use cases. In practice, many
developers use **interfaces** for object
shapes and **types** for everything else.

Let me know if you need further


clarification! 😊
Literal Types:
let bookingStatus : 'failed'|'pending'|'completed'

bookingStatus='failed' // here i can choose only failed ,pending and


completed

//another example

type bookStatus = 'failed'|'pending'|'completed'

const userStatus : bookStatus ='pending' //only among pending,failed


and completed can be choosen

//here how you can make your own type by mixing other types

type myType = string|number|{name:string , age?:number}|number[]|


string[]|(number|string)[];

// here my type can consists of any of these data types that are listed
after = sign

// (number|string)[] is an array contaning number and string for eg


[1,'abc',3]

// number[]|string[] is an array of either numbers and either strings for


eg: [1,2] or ['sdd', 'solid']

Types some good cases :

type person= {

name:string,

age: number
}

interface user extends person{ // interface can extend to type and


intrface both

id:number

const user1:user={

name:"banerjee",

age:23,

id:1

type coustomer = person & { //here you can only take type but not
interface

bud:number

const cust1:coustomer =

name:"ashim",

age:22,

bud:12330,

Example 2:

type people={ // no let var const here same is true for interfaces also

name:string,

age:number

}
let pep1:people={name:"ashim" ,age:23}; //here it will be let same is true
for interfaces also

const pep2:people={name:"ashim" ,age:23}; // here it will be const same


is true for interfaces also

var pep3: people={name:"can" ,age:23}; // here it will be var same is


true for interfaces also

more on types vs interfaces:


You're absolutely correct! **Types** in TypeScript are more
flexible than interfaces because they can define not only object
shapes but also **primitives**, **unions**, **tuples**, and
more. Let’s dive deeper into how types can define these
constructs and why they are more flexible than interfaces.

---

### **1. Defining Primitives**

Types can define aliases for primitive types like `string`,


`number`, `boolean`, etc.

#### Example:
```typescript
type Name = string;
type Age = number;
type IsActive = boolean;

const name: Name = "Alice";


const age: Age = 30;
const isActive: IsActive = true;
```

- Here, `Name`, `Age`, and `IsActive` are type aliases for


`string`, `number`, and `boolean`, respectively.

---

### **2. Defining Unions**

Types can define **union types**, which allow a value to be


one of several types.

#### Example:
```typescript
type ID = string | number;

const userId: ID = "abc123"; // Can be a string


const postId: ID = 456; // Can be a number
```

- Here, `ID` can be either a `string` or a `number`.

---

### **3. Defining Tuples**


Types can define **tuples**, which are arrays with a fixed
number of elements of specific types.

#### Example:
```typescript
type Point = [number, number];

const point: Point = [10, 20];


```

- Here, `Point` is a tuple representing a 2D point with two


numbers.

---

### **4. Defining Intersections**

Types can define **intersection types**, which combine


multiple types into one.

#### Example:
```typescript
type Person = {
name: string;
};
type Employee = {
id: number;
};

type EmployeeDetails = Person & Employee;

const employee: EmployeeDetails = {


name: "Alice",
id: 123
};
```

- Here, `EmployeeDetails` combines `Person` and `Employee`


into a single type.

---

### **5. Defining Complex Types**

Types can define complex types, including combinations of


primitives, objects, unions, and intersections.

#### Example:
```typescript
type Status = "active" | "inactive";
type User = {
id: number;
name: string;
status: Status;
};

type Admin = User & {


role: string;
};

const admin: Admin = {


id: 1,
name: "Alice",
status: "active",
role: "Admin"
};
```

- Here, `Status` is a union type, `User` is an object type, and


`Admin` is an intersection of `User` and an additional `role`
property.

---

### **6. Defining Function Types**

Types can define function signatures.

#### Example:
```typescript
type Add = (a: number, b: number) => number;

const add: Add = (a, b) => a + b;


```

- Here, `Add` is a type alias for a function that takes two


numbers and returns a number.

---

### **7. Defining Mapped Types**

Types can define **mapped types**, which transform the


properties of an existing type.

#### Example:
```typescript
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};

type Person = {
name: string;
age: number;
};
type ReadonlyPerson = Readonly<Person>;

const person: ReadonlyPerson = {


name: "Alice",
age: 30
};

// person.name = "Bob"; // Error: Cannot assign to 'name'


because it is a read-only property.
```

- Here, `Readonly` is a mapped type that makes all properties


of `Person` read-only.

---

### **8. Defining Conditional Types**

Types can define **conditional types**, which choose a type


based on a condition.

#### Example:
```typescript
type IsString<T> = T extends string ? true : false;

type A = IsString<"hello">; // true


type B = IsString<42>; // false
```

- Here, `IsString` is a conditional type that checks if `T` is a


`string`.

---

### **9. Defining Recursive Types**

Types can define **recursive types**, which reference


themselves.

#### Example:
```typescript
type TreeNode = {
value: number;
left?: TreeNode;
right?: TreeNode;
};

const tree: TreeNode = {


value: 1,
left: {
value: 2,
left: { value: 4 },
right: { value: 5 }
},
right: {
value: 3
}
};
```

- Here, `TreeNode` is a recursive type representing a binary


tree.

---

### **10. Defining Literal Types**

Types can define **literal types**, which are specific values.

#### Example:
```typescript
type Direction = "up" | "down" | "left" | "right";

const move: Direction = "up";


```

- Here, `Direction` is a union of literal types representing


specific strings.

---
### **Why Types Are More Flexible**

1. **Primitives:**
- Types can define aliases for primitive types, while interfaces
cannot.

2. **Unions and Intersections:**


- Types can define union and intersection types, while
interfaces cannot directly represent them.

3. **Tuples:**
- Types can define tuples, while interfaces cannot.

4. **Complex Types:**
- Types can define complex types, including combinations of
primitives, objects, unions, and intersections.

5. **Function Types:**
- Types can define function signatures, while interfaces are
primarily for object shapes.

6. **Mapped and Conditional Types:**


- Types can define mapped and conditional types, which are
not possible with interfaces.

7. **Recursive Types:**
- Types can define recursive types, while interfaces cannot.
---

### **Comparison with Interfaces**

| Feature | **Type** | **Interface**


|
|--------------------------|---------------------------------------|---------------------
-------------------|
| **Primitives** | Can define | Cannot
define |
| **Unions** | Can define | Cannot
define directly |
| **Tuples** | Can define | Cannot
define |
| **Intersections** | Can define | Cannot
define directly |
| **Function Types** | Can define | Cannot
define directly |
| **Mapped Types** | Can define | Cannot
define |
| **Conditional Types** | Can define | Cannot
define |
| **Recursive Types** | Can define | Cannot
define |
| **Declaration Merging** | Cannot be reopened |
Supports declaration merging |
| **Class Implementation** | Cannot be directly implemented
| Can be implemented by classes |

---
### **Conclusion**

Types are more flexible than interfaces because they can


define a wider range of constructs, including primitives, unions,
tuples, intersections, function types, mapped types, conditional
types, and recursive types. Use **types** when you need this
flexibility, and use **interfaces** for defining object shapes and
when you need declaration merging.

Let me know if you need further clarification! 😊

FUNCTIONS IN TYPESCRIPT;
In TypeScript, a function is a block of code that
performs a specific task. Functions can take inputs
(parameters) and return an output (return value).
TypeScript enhances JavaScript functions by adding
type annotations, which help ensure that the function is
used correctly.

Here’s a basic example of a function in TypeScript:

```typescript
function greet(name: string): string {
return `Hello, ${name}!`;
}
const message = greet("Alice");
console.log(message); // Output: Hello, Alice!
```

### Key Features of Functions in TypeScript:

1. **Parameter Types**: You can specify the types of


the function parameters.
2. **Return Type**: You can specify the type of the
value the function returns.
3. **Optional Parameters**: Parameters can be marked
as optional using `?`.
4. **Default Parameters**: You can provide default
values for parameters.
5. **Rest Parameters**: You can use rest parameters to
accept an indefinite number of arguments as an array.
6. **Arrow Functions**: TypeScript supports arrow
functions, which are a concise way to write functions.

### Examples:

1. **Function with Parameter Types and Return Type**:


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

const result = add(5, 3);


console.log(result); // Output: 8
```

2. **Optional Parameters**:
```typescript
function greet(name: string, greeting?: string): string
{
return greeting ? `${greeting}, ${name}!` :
`Hello, ${name}!`;
}

console.log(greet("Alice")); // Output: Hello, Alice!


console.log(greet("Bob", "Hi")); // Output: Hi, Bob!
```

3. **Default Parameters**:
```typescript
function greet(name: string, greeting: string =
"Hello"): string {
return `${greeting}, ${name}!`;
}
console.log(greet("Alice")); // Output: Hello, Alice!
console.log(greet("Bob", "Hi")); // Output: Hi, Bob!
```

4. **Rest Parameters**:
```typescript
function sum(...numbers: number[]): number {
return numbers.reduce((acc, curr) => acc + curr,
0);
}

console.log(sum(1, 2, 3, 4)); // Output: 10


```

5. **Arrow Functions**:
```typescript
const multiply = (a: number, b: number): number =>
a * b;

console.log(multiply(4, 5)); // Output: 20


```

6. **Function Overloading**:
TypeScript allows you to define multiple function
signatures for the same function, which is useful when
a function can accept different types or numbers of
parameters.

```typescript
function combine(a: string, b: string): string;
function combine(a: number, b: number): number;
function combine(a: any, b: any): any {
if (typeof a === 'string' && typeof b === 'string')
{
return a + b;
} else if (typeof a === 'number' && typeof b ===
'number') {
return a + b;
}
}

console.log(combine("Hello, ", "World!")); // Output:


Hello, World!
console.log(combine(5, 10)); // Output: 15
```

### Conclusion:
Functions in TypeScript are similar to those in
JavaScript but with added type safety. By using
TypeScript's type annotations, you can catch errors
early and make your code more predictable and easier
to understand.
Example and cases of fun overloading in ts
Example 1:
// function overloading in ts

function foo(arg1:number,arg2:string):string {

return arg2;

function foo(arg1:number,arg2:string):number { // this is dublicate function


implementation not the fucntion overloading

return arg1;

Example 2 :

// function overloading in ts

function foo(arg2:string):string {

return arg2;

function foo(arg1:number,arg2:number):number { //this will also show


dublicate function implementation

return arg1;

For example 3;

// function overloading in ts

function foo(arg1:number,arg2:string):string {
return arg2;

function foo(arg1:number,arg2:number):number { // this is dublicate function


implementation not the fucntion overloading

return arg1;

Example 4: this is valid

// Overload signatures

function processInput(input: string): string;

function processInput(input: number): number;

// Implementation

function processInput(input: string | number): string | number {

if (typeof input === 'string') {

return input.toUpperCase(); // Return a string

} else if (typeof input === 'number') {

return input * 2; // Return a number

throw new Error("Invalid input");

console.log(processInput("hello")); // Output: HELLO

console.log(processInput(10)); // Output: 20

q. I can do duntion implementation only by using the optional parameters then


why need function overloading ??
Type Guards:

Can be done by three methods

1. Typeof
2. Instanceof
3. Custom type guard function
Index Signature :

UNION AND INTERSECTION


// union

Case 1:

let val: "hello" | "bye" | "welcome";

val="xyz" //error: xyz is not assinable to type "hello" | "bye" |


"welcome" HERE I can only choose between hello ,bye or welcome

type x="hello" | "bye" | "welcome";

let a:x="xyz" // xyz is not assignable to x

Case 2:
type numstring= number|string ;

const value : numstring=34 //allowed

const value2: numstring="hello" //allowed

const value3: numstring= true //not allowed as it can accept only the
string and nymber values

Case 3:
type conobj={name:number,age:23|34}l;

const obj1:conobj ={

name:23

age:56 // this will be error as 56 cannot be assigned to a type 23|34

}
Case 4:

let array: (string | number)[] = ["apple",23, "banana"];

console.log(array); // Output: ["apple", 42, "banana", 100]

let arr:string[]| number[] =[23,21] // either all string or all numbers

(string |number)[] means array which contains string or number or both

Case 5:
type Dog = { bark(): void };

type Cat = { meow(): void };

function makeSound(animal: Dog | Cat) {

if ('bark' in animal) {

animal.bark();

} else {

animal.meow();

const dog: Dog = { bark: () => console.log("Woof!") };

const cat: Cat = { meow: () => console.log("Meow!") };

makeSound(dog); // Output: Woof!

makeSound(cat); // Output: Meow!


Intersection:
Case 1 :
type A = { a: string };

type B = { b: number };

type C = { c: boolean };

type Combined = (A | B) & C;

const obj1: Combined = { a: "hello", c: true };

const obj2: Combined = { b: 42, c: false };

const obj3:Combined ={a:"string" ,b:23,c:true};

const obj3:Combined ={c:true}; // Error at least one in A and B sgould be there

console.log(obj1); // Output: { a: "hello", c: true }

console.log(obj2); // Output: { b: 42, c: false }

console.log(obj3);

Case 2:
In TypeScript, the type string & number is
an intersection type that combines the
types string and number. However, this specific
intersection (string & number) is impossible to satisfy
because there is no value that can simultaneously be
both a string and a number.
Case 3:

type Person = { name: string };

type Employee = { id: number };

type EmployeeDetails = Person & Employee;


const employee: EmployeeDetails = { // it should contain both fields name as
well as id

name: "Alice",

id: 123,

};

console.log(employee); // Output: { name: "Alice", id: 123 }

Case 4:

You might also like