Mission 1: Be a typescript technocrat
Kazi Abdur Rakib
Posted on November 3, 2023
_
**
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
- List the available Node.js versions to see which versions are installed and available for use:
nvm ls
This will list the installed Node.js versions, and the currently active version will be indicated with an arrow.
- To switch to Node.js version 20.9.0, use the following command, replacing "20.9.0" with the specific version you want:
nvm use 20.9.0
This command will set the active Node.js version to 20.9.0.
- You can verify that you've switched to Node.js version 20.9.0 by running:
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:
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
Typescript config file
: tsc --init
then we ill get a tsconfig.json file, then we will edit it:
- search rootDir; then write typescritp file location in this rootDir
- search outDir, write path of my typescript file will store in which location.
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:
- Integer (int):
const age: number = 30;
- Floating-Point (float and double):
const pi: number = 3.14159;
- Boolean (bool):
const isTrue: boolean = true;
- Character (char):
In TypeScript, characters are represented as single-character strings:
const firstLetter: string = "A";
- String:
const greeting: string = "Hello, TypeScript!";
- Null:
const nullValue: null = null;
- Undefined (or None):
let undefinedValue: undefined;
- Void:
Functions with no return value have a void
type:
function sayHello(): void {
console.log("Hello, world!");
}
- Symbol:
const uniqueSymbol: symbol = Symbol("description");
-
Byte:
TypeScript doesn't have a specific byte data type, but you can work with bytes using
number
orUint8Array
. -
Date and Time:
TypeScript can work with date and time using JavaScript's
Date
object:
const currentDate: Date = new Date();
-
BigInteger and BigDecimal:
TypeScript supports large integers through the
BigInt
type:
const bigIntValue: bigint = 1234567890123456789012345678901234567890n;
-
Array:
Arrays in TypeScript can hold multiple values of the same data type:
const numbers: number[] = [1, 2, 3, 4, 5];
-
Tuple:
Tuples allow you to specify the types and order of elements in a fixed-size collection:
const person: [string, number] = ["Alice", 30];
-
Enum:
Enums define a set of named constant values:
enum Color { Red, Green, Blue, } const selectedColor: Color = Color.Red;
-
Any:
The
any
type is used when you want to opt out of TypeScript's type checking or when dealing with dynamic types:
let dynamicValue: any = 42; dynamicValue = "Hello, World!";
-
Union Types:
Union types allow variables to hold values of multiple types:
let mixedValue: number | string = 42; mixedValue = "Hello, World!";
-
Intersection Types:
Intersection types combine multiple types into one, allowing access to properties of each type:
type Employee = { name: string; role: string }; type Manager = { reports: number }; type ManagerEmployee = Employee & Manager; const manager: ManagerEmployee = { name: "Alice", role: "Manager", reports: 5 };
-
Object:
The
object
type represents non-primitive values, such as functions, arrays, and objects:
const user: object = { name: "Alice", age: 30 };
-
Function:
Functions in TypeScript can be defined with specific parameter and return types:
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.
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.
- Number: Used for numeric values (integers and floating-point numbers).
let age: number = 30;
let pi: number = 3.14;
- String: Used for text and character data.
let name: string = "John";
let message: string = 'Hello, TypeScript!';
- Boolean: Represents true or false values.
let isStudent: boolean = true;
let isWorking: boolean = false;
- Array: Used to store collections of values, and you can specify the type of elements it contains.
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:
- Object Type:
TypeScript allows you to define object types to represent structured data with specific properties and their types.
const person: { name: string; age: number } = {
name: "Alice",
age: 30,
};
- Optional Properties:
You can specify optional properties in object types using the ?
symbol.
const user: { name: string; age?: number } = {
name: "Bob",
// Age is optional, so it can be omitted
};
- Literal Types:
TypeScript allows you to define literal types where a variable can only have a specific, literal value.
const status: "active" | "inactive" = "active";
- Object with Optional and Literal Properties:
You can combine object types, optional properties, and literal types.
const person: { name: string; age?: number; status: "active" | "inactive" } = {
name: "Charlie",
status: "active",
};
- Type Alias:
You can create type aliases for object types to make your code more readable.
type Person = { name: string; age?: number };
const user: Person = {
name: "David",
// Age is optional
};
- Interface:
Interfaces are similar to type aliases but are often used when defining the shape of objects.
interface Product {
name: string;
price: number;
}
const item: Product = {
name: "Widget",
price: 10.99,
};
- Record Type:
You can use the Record
type to define object types with specific keys and their associated value types.
const phonebook: Record<string, string> = {
Alice: "555-1234",
Bob: "555-5678",
};
1-6 Function in typescript
1-7 Spread and Rest Operator
Certainly! Here are examples of the spread and rest operators in TypeScript:
- 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:
const arr1: number[] = [1, 2, 3]; const arr2: number[] = [4, 5, 6]; const combinedArray: number[] = [...arr1, ...arr2];
-
Object Spread:
const person = { name: "Alice", age: 30 }; const additionalInfo = { city: "Wonderland" }; const combinedPerson = { ...person, ...additionalInfo };
- 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:
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:
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.
}
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:
- Array Destructuring:
You can destructure arrays to extract individual values and assign them to variables:
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]
- Object Destructuring:
Object destructuring allows you to extract properties from objects and assign them to variables with the same name:
const person = {
name: "Alice",
age: 30,
city: "Wonderland",
};
const { name, age } = person;
console.log(name); // "Alice"
console.log(age); // 30
- Renaming Variables in Object Destructuring:
You can also rename variables while destructuring objects:
const person = {
name: "Bob",
age: 25,
};
const { name: fullName, age: years } = person;
console.log(fullName); // "Bob"
console.log(years); // 25
- Default Values in Object Destructuring:
You can provide default values for object properties that might not exist:
const person = {
name: "Charlie",
};
const { name, age = 30 } = person;
console.log(name); // "Charlie"
console.log(age); // 30
- Nested Destructuring:
Destructuring can also be used with nested objects:
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"
...rest
1-9 Type alias in typescript
1-10 Union and Intersection types
1-11 Ternary, optional chaining & nullish coalescing operator
1. nullish operator(??) its works for Null and undefined. not for "" .
1-12 Never,unknown and nullable type
Certainly! Here are examples of TypeScript data types and features related to never
, unknown
, and nullable types:
-
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.
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {
// Infinite loop
}
}
-
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.
let userInput: unknown;
let username: string;
userInput = "Alice";
if (typeof userInput === "string") {
username = userInput;
}
- Nullable Types:
TypeScript allows you to make a type nullable using the null
and undefined
values.
let numberOrNull: number | null = null;
let stringOrUndefined: string | undefined = "Hello";
-
Using
null
andundefined
Together:
You can create a type that allows both null
and undefined
values.
let value: number | null | undefined = null;
-
Type Assertion for
unknown
:
When working with unknown
, you can use type assertions to tell TypeScript to treat a value as a specific type.
let userInput: unknown = "Bob";
let username: string = userInput as string;
- 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.
let possiblyNull: string | null = null;
// Error: Object is possibly 'null'.
console.log(possiblyNull.length);
- Non-Nullable Assertion:
You can use the non-nullable assertion operator !
to tell TypeScript that a value is not null or undefined.
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.
- Nullable type
- unknown type 3.Never
========================================================================
*## 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.
{
//
// 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.
// interface
type User1 = {
name: string;
age: number;
};
type UserWithRole1 = User1 & { role: string };
const user1: UserWithRole1 = {
name: "Persian",
age: 100,
role: "manager",
};
interface User2 {
name: string;
age: number;
}
interface UserWithRole2 extends User2 {
role: string;
}
const user1: UserWithRole2 = {
name: "Persian",
age: 100,
role: "manager",
};
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
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
{
// 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" },
];
}
// 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
{
// 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
{
// 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
{
// 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:
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.
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.
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.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:
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
{
// 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
{
//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
type AreaString<T> = {
[key in keyof T]: T[key];
};
{
// 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,
};
//
}
// 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:
-
Partial<T>
: Makes all properties of a typeT
optional.
Certainly, here's a more detailed example of how to use the Partial<T>
utility type in 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 likeid
,username
,email
, andage
.We create a complete user object
completeUser
with all properties defined.We use the
Partial<User>
utility type to create a new typePartialUser
where all properties of theUser
type are optional.We create a partial user object
partialUser
of typePartialUser
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 thecountry
property.We create another partial user
partialUserWithAge
with theage
property defined.Finally, we merge a complete user (
completeUser
) with a partial user with anage
property (partialUserWithAge
) to create a new user object (mergedUser
). This showcases how you can combine complete and partial objects to get a merged result.
-
Required<T>
: Makes all properties of a typeT
required. Certainly, here's an example of theRequired<T>
utility type in 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 typeRequiredUser
where all properties are required.We create an object
user
of typeRequiredUser
with all properties present, which is valid.Attempting to create an object
incompleteUser
of typeRequiredUser
with missing properties results in a TypeScript type error, enforcing that all properties are required for objects of this type.
-
Readonly<T>
: Makes all properties of a typeT
read-only.
Certainly, here's an example of the Readonly<T>
utility type in 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 propertiesname
andage
.We use the
Readonly<T>
utility type to create a new typeReadonlyPerson
, which makes all properties ofPerson
read-only.We create an object
person
of typeReadonlyPerson
. Attempting to modify any of its properties, such asname
andage
, results in TypeScript type errors. This ensures that the object remains immutable and read-only once it's defined.
-
Record<K, T>
: Creates an object type with keys of typeK
and values of typeT
.
Certainly, here's an example of how to use the Record<K, T>
utility type in 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 aproductId
and aquantity
.We use the
Record<K, T>
utility type to create a typeShoppingCart
, which represents a shopping cart with keys of typestring
(product IDs) and values of typeCartItem
.We create a
shoppingCart
object with some items, where each item is associated with a product ID (string key) and aCartItem
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.
-
Pick<T, K>
: Selects specific properties from a typeT
.
Certainly! Here's an example of how to use the Pick<T, K>
utility type in 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 likeid
,username
,email
, andage
.We use the
Pick
utility type to create a new typeUserIdentity
by selecting specific properties"id"
and"username"
from theUser
type.We then create an object
user
of typeUserIdentity
, 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.
-
Omit<T, K>
: Excludes specific properties from a typeT
.
Certainly, here's an example of how to use the Omit
utility type in 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 likeid
,username
,email
, andage
.We use the
Omit
utility type to create a new typeUserWithoutEmail
by excluding the property"email"
from theUser
type.We then create an object
user
of typeUserWithoutEmail
, 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.
-
Exclude<T, U>
: Excludes values in typeU
from typeT
.
Certainly! Here's an example of how to use the Exclude<T, U>
utility type in 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 typeSecondaryColors
that excludes values fromAllColors
that are assignable toPrimaryColors
. This effectively gives us the secondary colors.We assign the value
"Green"
tosecondaryColor
, which is valid because it's a secondary color.Attempting to assign the value
"Red"
toinvalidColor
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.
-
Extract<T, U>
: Extracts values in typeU
from typeT
.
Certainly, here's an example of how to use the Extract
utility type in 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 typeCommonShape
that extracts shapes common to bothShape
andFilledShape
.We create a variable
shape
of typeCommonShape
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
andFilledShape
, such as "triangle," will result in a TypeScript type error, ensuring type safety.
-
NonNullable<T>
: Excludesnull
andundefined
from typeT
.
Certainly! Here's an example of how to use the NonNullable<T>
utility type in 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 includesstring
,null
, andundefined
, which means it can hold either a valid string or null or undefined.We use the
NonNullable<NullableString>
utility type to create a new typeNonNullString
, which excludesnull
andundefined
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 assignnull
orundefined
to them results in TypeScript type errors, ensuring that only valid strings can be assigned to these variables.
-
ReturnType<T>
: Obtains the return type of a function typeT
.
Certainly! Here's an example of how to use the ReturnType<T>
utility type in 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:
We define a function
greet
that takes aname
parameter and returns a string greeting.We use the
ReturnType<typeof greet>
utility type to capture the return type of thegreet
function, which isstring
.We create a variable
message
of typeGreeting
, which is equivalent tostring
.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 ofReturnType
for ensuring the correct return type of functions.Parameters<T>
: Obtains the parameter types of a function typeT
as a tuple.
Certainly, here's an example of how to use the Parameters<T>
utility type in 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 typenumber
and a return type ofnumber
.We use the
Parameters
utility type to extract the parameter types from theAddFunction
type. This results in theAddFunctionArgs
type, which is a tuple of the parameter types.We create a function
add
of typeAddFunction
that takes two parameters,a
andb
, and returns their sum.We use the
add
function with valid parameter types (number
) to compute the sum, which is7
.We also attempt to use the
add
function with invalid parameter types (string
andnumber
), 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.
-
InstanceType<T>
: Obtains the instance type of a constructor function typeT
.
Certainly, here's an example of how to use the InstanceType<T>
utility type in 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 aname
and aprice
.We use the
InstanceType<typeof Product>
utility type to obtain the instance type of theProduct
class. This allows us to create instances with the same shape asProduct
.We create an instance
product
of typeProductInstance
, 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
3-1: Class and object
{
// 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.
{
// 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
"role" in user
typeof param1 === "number"
{
// 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
animal instanceof Dog;
animal instanceof Cat;
const isDog = (animal: Animal): animal is Dog => {
return animal instanceof Dog;
};
{
// 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
public readonly id: number;
public name: string;
private _balance: number;
this._balance = balance;
{
// 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
set deposit(amount: number) {
this._balance = this.balance + amount;
}
get balance() {
return this._balance;
}
{
// 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
=> 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);
=>if use static in the function then during the log we have to use classname dot function.
static increment() {
}
console.log(Counter.increment());
{
// 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
{
// 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
`interface Vehicle1 `
`class Car1 implements Vehicle1`
`abstract class Car2`
`class ToyotaCar extends Car2 `
abstract class Car2 {
I am just testing
abstract startEngine(): void;
abstract stopEngine(): void;
abstract move(): void;
test() {
console.log();
}
}
{
// 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
public readonly id: number;
public name: string;
protected _balance: number;
private getBalance() {
return this._balance;
}
getHiddenMethod(){
return this.getBalance()
}
---
class StudentAccount extends BankAccount{
test(){
this.
}
{
// 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.
//
}
Posted on November 3, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.