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: