├── .DS_Store ├── README.md └── images └── access-modifiers.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anisul-Islam/typescript-documentation/05baea476f0b578459eb746ce980c3e24ca5c8c4/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeScript Documentation 2 | 3 | - prerequisities: Javascript 4 | 5 | ## Table of Contents 6 | 7 | 1. [Basic Typescript Topics](#1-basic-typescript-topics) 8 | 9 | [1.1 Introduction to Typescript](#11-introduction-to-typescript) 10 | 11 | [1.2 Data Types: Built-in / Basic Types](#12-data-types-built-in--basic-types) 12 | 13 | [1.3 Data Types: User defined types](#13-data-types-user-defined) 14 | 15 | [1.4 tsconfig](#14-tsconfig) 16 | 17 | [1.5 function](#15-function) 18 | 19 | 2. [Intermediate TypeScript Topics](#2-intermediate-typescript-topics) 20 | 21 | [2.1 Creating types from types](#21-creating-types-from-types) 22 | 23 | [2.2 Narrowing](#22-narrowing) 24 | 25 | [2.3 Type guards](#23-type-guards-example) 26 | 27 | [2.4 DOM Manipulation with typescripts](#24-dom-manipulation-with-typescript) 28 | 29 | 3. [Advanced TypeScript Topics]() 30 | 31 | ## 1. Basic Typescript Topics 32 | 33 | ### 1.1 Introduction to Typescript 34 | 35 | What is TypeScript? 36 | 37 | - In a simple words, Additional Types (tuples, enum, interfaces, generics etc) + JavaScript = TypeScript 38 | - It is a superset of JS 39 | - developed (2012) and maintained by Microsoft 40 | - we can use typeof to check data type 41 | - Js is dynamically types -> let x = 20; 42 | - Typescript add static typing to js -> let x: number = 20; 43 | 44 | Why TypeScript? 45 | 46 | - JS Check types in run time while typescript add static typing to JS so we can handle errors before running the program. We can handle errors beofre running the program. 47 | - increase readability and code quality 48 | - We can use it React, Vue, popular JS libraray Angular use TypeScript. 49 | - It can be used in both: client and server side. 50 | - Intellisense IDE Support while coding: code completion, content assist and code hinting 51 | 52 | TS Versions 53 | 54 | - earlier versions 55 | - TypeScript 1.0 October 2014 56 | - TypeScript 2.0 September 2016 57 | - TypeScript 3.0 July 2018 58 | - TypeScript 4.0 - latest release August 2020 59 | - TypeScript 5.4 - 2024 60 | 61 | Code Example of Javascript and TypeScript 62 | 63 | ```js 64 | // index.js 65 | // without typescript 66 | function addNumbers(num1, num2) { 67 | console.log(num1 + num2); 68 | } 69 | 70 | addNumbers(20, 30); 71 | addNumbers(20, '30'); 72 | 73 | // with typescript 74 | // without typescript 75 | function addNumbers(num1: number, num2: number) { 76 | console.log(num1 + num2); 77 | } 78 | 79 | addNumbers(20, 30); // no error 80 | addNumbers(20, '30'); // error 81 | 82 | // without typescript 83 | let x; 84 | x = 20; // correct 85 | x = 'anisul'; // correct 86 | x = true; // correct 87 | x = [10, 20]; // correct 88 | 89 | // with typescript 90 | let x: number; 91 | x = 20; // correct 92 | x = '20'; // Not correct 93 | x = true; // Not correct 94 | x = [10, 20]; // Not correct 95 | ``` 96 | 97 | How does typescript work? 98 | 99 | - index.ts -> tsc index.ts -> index.js 100 | 101 | Environment setup 102 | 103 | - Install node & typescript 104 | 105 | ```js 106 | local installation: npm intsall typescript --save-dev 107 | Or 108 | global installation: npm install -g typescript 109 | ``` 110 | 111 | - check various versions: 112 | 113 | ```js 114 | node --version 115 | npm --version 116 | tsc --version 117 | ``` 118 | 119 | First typescript program 120 | 121 | - typescript file extension is .ts 122 | - Run the following program using `tsc index.ts --watch` command and then `node index.js` 123 | 124 | ```js 125 | // index.ts 126 | 127 | // without ts 128 | function addNumbers(num1, num2) { 129 | console.log(num1 + num2); 130 | } 131 | 132 | addNumbers(20, 30); 133 | addNumbers(20, "30"); 134 | 135 | // correct one using ts 136 | function addNumbers(num1: number, num2:number) { 137 | console.log(num1 + num2); 138 | } 139 | 140 | addNumbers(20, 30); 141 | addNumbers(20, "30"); 142 | 143 | 144 | let num1 = 20; 145 | console.log(num1); 146 | 147 | let name= "anisul islam"; 148 | name. //intellisense support will be here 149 | ``` 150 | 151 | ### 1.2 Data Types: Built-in / Basic Types 152 | 153 | - Any (super type) 154 | - built in types: number, string, boolean, void, null, undefined, never 155 | - user-defined types: Arrays, Enums, Classes, interfaces etc. 156 | - for avoiding typescript in entire file: 157 | `// @ts-nocheck` 158 | 159 | In TypeScript, you can use basic types to specify the type of variables, function parameters, and return values. Here are some of the basic types in TypeScript: 160 | 161 | 1. **number**: Represents both integer and floating-point numbers. 162 | 163 | ```typescript 164 | let age: number = 25; 165 | let price: number = 9.99; 166 | ``` 167 | 168 | 2. **string**: Represents a sequence of characters. 169 | 170 | ```typescript 171 | let name: string = 'John'; 172 | ``` 173 | 174 | 3. **boolean**: Represents a true or false value. 175 | 176 | ```typescript 177 | let isDone: boolean = false; 178 | ``` 179 | 180 | 4. **any**: Represents a dynamic or untyped value. Avoid using this when possible, as it bypasses type checking. if you have no knowledge about the variable type use any type: user input values 181 | 182 | ```typescript 183 | let data: any = 42; 184 | data = 'Hello'; 185 | 186 | let password: any; 187 | let passwords: any[]; 188 | ``` 189 | 190 | 5. **void**: Represents the absence of a value, typically used as the return type of functions that don't return anything. 191 | 192 | ```typescript 193 | function logMessage(): void { 194 | console.log('This is a log message.'); 195 | } 196 | ``` 197 | 198 | 6. **null** and **undefined**: Represent null and undefined values, respectively. 199 | 200 | ```typescript 201 | let n: null = null; 202 | let u: undefined = undefined; 203 | ``` 204 | 205 | 7. **never**: Represents a value that never occurs, such as a function that throws an error or an infinite loop. 206 | 207 | ```typescript 208 | function throwError(message: string): never { 209 | throw new Error(message); 210 | } 211 | ``` 212 | 213 | These basic types provide a foundation for specifying the types of variables and data in TypeScript. You can use them to ensure type safety in your code and catch type-related errors at compile time. 214 | 215 | - inferred Typing 216 | 217 | ```js 218 | let userName = 'anis'; // data type inferred as string 219 | ``` 220 | 221 | ### 1.3 Data Types: User defined 222 | 223 | 1. **union**: Union Type - more than one type for variable or function parameter. Program - odd/even for number and string to give idea about this union concept. 224 | 225 | ```js 226 | let userId: string | number; 227 | 228 | // userId = 101; // no error 229 | // userId = "101"; // no error 230 | // userId = true; // error 231 | 232 | function userIdDataType(userId: string | number) { 233 | console.log(typeof userId); 234 | } 235 | 236 | userIdDataType('123'); // no error 237 | userIdDataType(123); // no error 238 | // userIdDataType(true); // error 239 | 240 | const isEven = (num: number | string) => { 241 | if (typeof num === 'number') { 242 | console.log(typeof num); 243 | return num % 2 === 0 ? 'even' : 'odd'; 244 | } else { 245 | console.log(typeof num); 246 | return Number(num) % 2 === 0 ? 'even' : 'odd'; 247 | } 248 | }; 249 | 250 | console.log(isEven(32)); 251 | console.log(isEven('32')); 252 | ``` 253 | 254 | 2. **object**: Represents any non-primitive value. 255 | 256 | ```typescript 257 | let person: object = { name: 'Alice', age: 30 }; 258 | 259 | let user: { 260 | name: string; 261 | age: number; 262 | }; 263 | 264 | user = { 265 | name: 'anisul islam', 266 | age: 32, 267 | }; 268 | 269 | let names: object; 270 | names = { name1: 'anis' }; 271 | console.log(names); 272 | 273 | let users: object[]; 274 | users = []; 275 | 276 | let user1: { userName: string; userId: number }; 277 | user1 = { userName: 'anis', userId: 101 }; 278 | users.push(user1); 279 | 280 | let user2: { userName: string; userId: number }; 281 | user2 = { userName: 'rabu', userId: 102 }; 282 | 283 | users.push(user2); 284 | 285 | for (const key in users) { 286 | console.log(users[key]['userName']); 287 | } 288 | ``` 289 | 290 | 3. **array**: Represents an array of values of a specific type. 2 ways to declare: `number[]` or `Array` 291 | 292 | ```typescript 293 | let numbers: number[] = [1, 2, 3, 4, 5]; 294 | 295 | // let users = ["anis", "rabu", "pinky"]; 296 | 297 | // let users: string[]; 298 | // users = ["anis", "rabu", "pinky"]; 299 | 300 | let users: Array; 301 | users = ['anis', 'rabu', 'pinky']; 302 | 303 | // for (let index = 0; index < users.length; index++) { 304 | // const element = users[index]; 305 | // console.log(element); 306 | // } 307 | 308 | // users.forEach((element) => { 309 | // console.log(element); 310 | // }); 311 | 312 | users.sort(); 313 | console.log(users); 314 | 315 | users.push('limon'); 316 | console.log(users); 317 | 318 | users.pop(); 319 | console.log(users); 320 | 321 | users.unshift('milton'); 322 | console.log(users); 323 | 324 | users.shift(); 325 | console.log(users); 326 | 327 | // multi-types array 328 | // let users: (number | string)[]; 329 | // users = [10, "anis", 25, 35, "islam"]; 330 | ``` 331 | 332 | 4. **tuple**: Represents an array with a fixed number of elements, each with a specific type. 333 | 334 | ```typescript 335 | let employee: [string, number] = ['John Doe', 30]; 336 | 337 | let users: [number, String]; 338 | users = [101, 'anis']; 339 | 340 | console.log(users); 341 | console.log(users[0]); 342 | console.log(users[1]); 343 | 344 | users.push(102, 'sakib'); 345 | console.log(users); 346 | ``` 347 | 348 | 5. **enum**: Represents a set of named constants. no duplicate data. 349 | 350 | ```typescript 351 | enum Color { 352 | Red, 353 | Green, 354 | Blue, 355 | } 356 | 357 | let selectedColor: Color = Color.Red; 358 | 359 | // enum example 360 | // helps us to store constants 361 | 362 | // numeric enum 363 | enum UserRequest { 364 | ReadData, 365 | // ReadData = 2, 366 | SaveData, 367 | UpdateData, 368 | } 369 | 370 | console.log(UserRequest); 371 | console.log(UserRequest.ReadData); 372 | console.log(UserRequest.SaveData); 373 | 374 | // string enum 375 | enum UserRequest { 376 | ReadData = 'READ_DATA', 377 | // ReadData = 2, 378 | SaveData = 'SAVE_DATA', 379 | UpdateData = 'UPDATE_DATA', 380 | } 381 | 382 | console.log(UserRequest); 383 | console.log(UserRequest.ReadData); 384 | console.log(UserRequest.SaveData); 385 | console.log(UserRequest['UpdateData']); 386 | 387 | // Heterogeneous enum 388 | enum User { 389 | id = 101, 390 | name = 'anisul', 391 | } 392 | ``` 393 | 394 | 6. **Intersection**: In TypeScript, you can use intersection types to combine multiple types into a single type that has all the properties and methods of each type. Intersection types are created using the `&` operator. Here's an example: 395 | 396 | ```typescript 397 | // Define two types 398 | type Employee = { 399 | name: string; 400 | role: string; 401 | }; 402 | 403 | type Manager = { 404 | department: string; 405 | employeesManaged: number; 406 | }; 407 | 408 | // Create an intersection type 409 | type ManagerWithEmployeeInfo = Employee & Manager; 410 | 411 | // Create an object that conforms to the intersection type 412 | const manager: ManagerWithEmployeeInfo = { 413 | name: 'Alice', 414 | role: 'Manager', 415 | department: 'HR', 416 | employeesManaged: 10, 417 | }; 418 | 419 | // Access properties 420 | console.log(manager.name); // Alice 421 | console.log(manager.role); // Manager 422 | console.log(manager.department); // HR 423 | console.log(manager.employeesManaged); // 10 424 | ``` 425 | 426 | In this example, we define two types: `Employee` and `Manager`. Then, we create an intersection type called `ManagerWithEmployeeInfo` by combining `Employee` and `Manager` using the `&` operator. The resulting type, `ManagerWithEmployeeInfo`, has all the properties of both `Employee` and `Manager`. 427 | 428 | When we create an object (`manager`) that conforms to the `ManagerWithEmployeeInfo` type, it must have all the properties defined in both `Employee` and `Manager`. This allows us to create objects that have a combination of properties from different types, providing flexibility and type safety. 429 | 430 | Intersection types are especially useful when you want to compose types to represent complex objects or data structures in your TypeScript code. 431 | 432 | 7. **Custom Type**: you can create your own type 433 | 434 | ```js 435 | type User = { userName: string, userId: number }; 436 | 437 | let users: User[]; 438 | users = []; 439 | 440 | let user1: User; 441 | user1 = { userName: 'anis', userId: 101 }; 442 | users.push(user1); 443 | 444 | let user2: User; 445 | user2 = { userName: 'rabu', userId: 102 }; 446 | users.push(user2); 447 | 448 | let user3: User; 449 | user3 = { userName: 'lucky', userId: 103 }; 450 | users.push(user3); 451 | 452 | // console.log(users); 453 | 454 | type RequestType = 'GET' | 'POST'; 455 | let getRequest: RequestType; 456 | getRequest = 'GET'; 457 | 458 | function requestHandler(requestType: RequestType) { 459 | console.log(requestType); 460 | } 461 | requestHandler('GET'); 462 | ``` 463 | 464 | 8. **class Type OOP** - You can create class type as well. class can have constructor, properties, methods. create object let objectName = new className() 465 | 466 | - Class and constructor Example 467 | 468 | ```js 469 | class User { 470 | // properties, methods, constructor 471 | userName: string; 472 | age: number; 473 | 474 | constructor(userName: string, age: number) { 475 | this.userName = userName; 476 | this.age = age; 477 | } 478 | 479 | display(): void { 480 | console.log(`username: ${this.userName}, age: ${this.age}`); 481 | } 482 | } 483 | 484 | let user1 = new User('Anisul Islam', 25); 485 | user1.display(); 486 | 487 | let user2 = new User('Rabeya Islam', 31); 488 | user2.display(); 489 | ``` 490 | 491 | - Inheritance: inheritance helps us to acquire properties of one class to another 492 | 493 | ```js 494 | class User { 495 | userName: string; 496 | age: number; 497 | 498 | constructor(userName: string, age: number) { 499 | this.userName = userName; 500 | this.age = age; 501 | } 502 | 503 | display(): void { 504 | console.log(`username: ${this.userName}, age: ${this.age}`); 505 | } 506 | } 507 | 508 | class Student extends User { 509 | studentId: number; 510 | 511 | constructor(userName: string, age: number, studentId: number) { 512 | super(userName, age); 513 | this.studentId = studentId; 514 | } 515 | display(): void { 516 | console.log( 517 | `username: ${this.userName}, age: ${this.age}, id: ${this.studentId}` 518 | ); 519 | } 520 | } 521 | 522 | let student1 = new Student('keya', 31, 1302020015); 523 | student1.display(); 524 | 525 | let user1 = new User('Anisul Islam', 25); 526 | user1.display(); 527 | 528 | // let user2 = new User("Rabeya Islam", 31); 529 | // user2.display(); 530 | ``` 531 | 532 | - **Abstract class** - abstraction helps us to hide the implementation of something. class declared with abstract keyword. object can not be created from abstract class. if a class extends abstract class; it must inherit all the abstract methods 533 | 534 | ```js 535 | abstract class User { 536 | userName: string; 537 | age: number; 538 | 539 | constructor(userName: string, age: number) { 540 | this.userName = userName; 541 | this.age = age; 542 | } 543 | 544 | abstract display(): void; 545 | } 546 | 547 | class Student extends User { 548 | studentId: number; 549 | 550 | constructor(userName: string, age: number, studentId: number) { 551 | super(userName, age); 552 | this.studentId = studentId; 553 | } 554 | display(): void { 555 | console.log( 556 | `username: ${this.userName}, age: ${this.age}, id: ${this.studentId}` 557 | ); 558 | } 559 | } 560 | 561 | let student1 = new Student("keya", 31, 1302020015); 562 | student1.display(); 563 | 564 | ``` 565 | 566 | - **Encapsulation and access modifiers** - 4 key principles of Object Oriented Programming (OOP): Inheritance, Abstraction, Encapsulation, Polymorphism. Encapsulation helps us to manage the visibility of class members. 4 Access modifiers: public, private, protected, readonly 567 | 568 | ![Access Modifiers](images/access-modifiers.png) 569 | 570 | ```js 571 | // public, private, protected, readonly 572 | // setter and getter 573 | class User { 574 | readonly userName: string; 575 | public age: number; 576 | 577 | constructor(userName: string, age: number) { 578 | this.userName = userName; 579 | this.age = age; 580 | } 581 | 582 | display(): void { 583 | console.log(`username: ${this.userName}, age: ${this.age}`); 584 | } 585 | } 586 | 587 | class Student extends User { 588 | private studentId: number; 589 | 590 | constructor(userName: string, age: number, studentId: number) { 591 | super(userName, age); 592 | this.studentId = studentId; 593 | } 594 | display(): void { 595 | console.log( 596 | `username: ${this.userName}, age: ${this.age}, id: ${this.studentId}` 597 | ); 598 | } 599 | 600 | setStudentId(studentId: number): void { 601 | this.studentId = studentId; 602 | } 603 | 604 | getStudentId(): number { 605 | return this.studentId; 606 | } 607 | } 608 | 609 | let student1 = new Student("keya", 31, 1302020015); 610 | student1.setStudentId(1302020017); 611 | console.log(student1.getStudentId()); 612 | // student1.display(); 613 | 614 | let user1 = new User("robi", 23); 615 | console.log(user1.userName); 616 | // user1.display(); 617 | ``` 618 | 619 | ### Inheritance 620 | 621 | Inheritance allows a class to inherit properties and methods from another class. 622 | 623 | ### Encapsulation 624 | 625 | Encapsulation restricts direct access to some of an object's components, which can be achieved using access modifiers (`private`, `protected`, `public`). 626 | 627 | ### Polymorphism 628 | 629 | Polymorphism allows methods to do different things based on the object it is acting upon, even though they share the same name. 630 | 631 | ### Abstraction 632 | 633 | Abstraction allows you to define methods that must be created within any child classes built from the abstract class. 634 | 635 | Here’s an example that demonstrates all four principles: 636 | 637 | ```typescript 638 | // Abstract class (Abstraction) 639 | abstract class Person { 640 | private _name: string; 641 | private _age: number; 642 | 643 | constructor(name: string, age: number) { 644 | this._name = name; 645 | this._age = age; 646 | } 647 | 648 | get name(): string { 649 | return this._name; 650 | } 651 | 652 | get age(): number { 653 | return this._age; 654 | } 655 | 656 | // Abstract method 657 | abstract getDescription(): string; 658 | } 659 | 660 | // Student class (Inheritance) 661 | class Student extends Person { 662 | private _grade: number; 663 | 664 | constructor(name: string, age: number, grade: number) { 665 | super(name, age); 666 | this._grade = grade; 667 | } 668 | 669 | get grade(): number { 670 | return this._grade; 671 | } 672 | 673 | // Implementing abstract method (Polymorphism) 674 | getDescription(): string { 675 | return `Student: ${this.name}, Age: ${this.age}, Grade: ${this.grade}`; 676 | } 677 | } 678 | 679 | // Teacher class (Inheritance) 680 | class Teacher extends Person { 681 | private _subject: string; 682 | 683 | constructor(name: string, age: number, subject: string) { 684 | super(name, age); 685 | this._subject = subject; 686 | } 687 | 688 | get subject(): string { 689 | return this._subject; 690 | } 691 | 692 | // Implementing abstract method (Polymorphism) 693 | getDescription(): string { 694 | return `Teacher: ${this.name}, Age: ${this.age}, Subject: ${this.subject}`; 695 | } 696 | } 697 | 698 | // Create instances and demonstrate polymorphism 699 | const student = new Student("Alice", 20, 90); 700 | const teacher = new Teacher("Bob", 45, "Mathematics"); 701 | 702 | console.log(student.getDescription()); // Student: Alice, Age: 20, Grade: 90 703 | console.log(teacher.getDescription()); // Teacher: Bob, Age: 45, Subject: Mathematics 704 | 705 | // Encapsulation: attempting to access private properties directly will result in an error 706 | // console.log(student._name); // Error: Property '_name' is private and only accessible within class 'Person'. 707 | // console.log(teacher._subject); // Error: Property '_subject' is private and only accessible within class 'Teacher'. 708 | 709 | // Use getter methods to access encapsulated properties 710 | console.log(student.name); // Alice 711 | console.log(teacher.subject); // Mathematics 712 | ``` 713 | 714 | ### Explanation 715 | 716 | 1. **Abstraction**: 717 | - The `Person` class is abstract and contains an abstract method `getDescription`. 718 | - This method must be implemented by any non-abstract subclass of `Person`. 719 | 720 | 2. **Encapsulation**: 721 | - Private properties (`_name`, `_age`, `_grade`, `_subject`) are used to restrict direct access. 722 | - Getter methods (`get name()`, `get age()`, `get grade()`, `get subject()`) are provided to access these private properties. 723 | 724 | 3. **Inheritance**: 725 | - The `Student` and `Teacher` classes inherit from the `Person` class. 726 | - They call the constructor of the parent class using `super(name, age)`. 727 | 728 | 4. **Polymorphism**: 729 | - Both `Student` and `Teacher` implement the `getDescription` method from the `Person` class. 730 | - This method behaves differently based on whether it's called on a `Student` or `Teacher` instance. 731 | 732 | This example demonstrates how you can use these OOP principles in TypeScript to create a well-structured and maintainable codebase. 733 | 734 | 9. **Interface type** 735 | 736 | ```js 737 | // without interface 738 | let users: { 739 | id: number, 740 | name: string, 741 | age: number, 742 | }[] = []; 743 | 744 | let user1: { 745 | id: number, 746 | name: string, 747 | age: number, 748 | } = { 749 | id: 1, 750 | name: 'Mr. Potato', 751 | age: 32, 752 | }; 753 | 754 | let user2: { 755 | id: number, 756 | name: string, 757 | age: number, 758 | } = { id: 2, name: 'Ms. Tomato', age: 21 }; 759 | 760 | users.push(user1); 761 | users.push(user2); 762 | 763 | const printUserInfo = (user: { id: number, name: string, age: number }) => { 764 | console.log(`userid = ${user.id}, name = ${user.name}, age = ${user.age}`); 765 | }; 766 | 767 | users.forEach((user) => printUserInfo(user)); 768 | 769 | // with interface 770 | interface User { 771 | id: number; 772 | name: string; 773 | age: number; 774 | } 775 | 776 | let users: User[] = []; 777 | 778 | let user1: User = { id: 1, name: 'Mr. Potato', age: 32 }; 779 | let user2: User = { id: 2, name: 'Ms. Tomato', age: 21 }; 780 | 781 | users.push(user1); 782 | users.push(user2); 783 | 784 | const printUserInfo = (user: User) => { 785 | console.log(`userid = ${user.id}, name = ${user.name}, age = ${user.age}`); 786 | }; 787 | 788 | users.forEach((user) => printUserInfo(user)); 789 | ``` 790 | 791 | ```js 792 | // class implements interface 793 | interface UserFormatter { 794 | formatUser: () => string; 795 | } 796 | 797 | export class User implements UserFormatter { 798 | constructor(private fullName: string, private age: number) {} 799 | 800 | formatUser = () => { 801 | return `name: ${this.fullName}, age: ${this.age}`; 802 | }; 803 | } 804 | 805 | let user = new User("Mr. Potato", 32); 806 | console.log(user); 807 | console.log(user.formatUser()); 808 | ``` 809 | 810 | 10. **Interface vs type** 811 | 812 | - both are nearly similar in most cases. 813 | - However, Adding new filed after creation is possible for an interface but not possible for a type. 814 | 815 | ```js 816 | // Example 1 817 | interface Color { 818 | title: string; 819 | } 820 | interface Color { 821 | text: string; 822 | } 823 | // now class A has access to title and string 824 | class A implements Color { 825 | title: string; 826 | text: string; 827 | } 828 | ``` 829 | 830 | - both can be extended 831 | 832 | ```js 833 | interface IFUser { 834 | name: string; 835 | } 836 | 837 | interface IFStudent extends IFUser { 838 | student_id: string; 839 | } 840 | 841 | // Extending a type via intersections 842 | type User = { 843 | name: string, 844 | }; 845 | 846 | type Student = User & { 847 | student_id: string, 848 | }; 849 | 850 | let s1: Student; 851 | s1 = { 852 | name: 'anisul islam', 853 | student_id: '1302', 854 | }; 855 | ``` 856 | 857 | ```js 858 | interface IFUser { 859 | name: string; 860 | } 861 | 862 | interface IFStudent extends IFUser { 863 | student_id: string; 864 | } 865 | 866 | class Student implements IFStudent { 867 | name: string; 868 | student_id: string; 869 | 870 | constructor(name, student_id) { 871 | this.name = name; 872 | this.student_id = student_id; 873 | } 874 | 875 | printDetails = () => { 876 | return `${this.name}, ${this.student_id}`; 877 | }; 878 | } 879 | 880 | const s1 = new Student('anisul islam', '1302020017'); 881 | console.log(s1.printDetails()); 882 | ``` 883 | 884 | ### 1.4 tsconfig 885 | 886 | - create src, public folder 887 | - Inside public folder create index.html, style.css and inside src folder create index.ts or other ts files 888 | - in terminal -> tsc --init 889 | - edit tsconfig.json as shown in the following example 890 | 891 | ```json 892 | { 893 | "compilerOptions": { 894 | "target": "es5", 895 | "module": "commonjs", 896 | "rootDir": "./src", 897 | "outDir": "./public", 898 | "strict": true, 899 | "noUnusedLocals": true, 900 | "noUnusedParameters": true 901 | }, 902 | "include": ["./src"], 903 | "files": ["./src/index.ts", "./src/app.ts"] 904 | } 905 | ``` 906 | 907 | - run the compiler: tsc 908 | 909 | ### 1.5 function 910 | 911 | Explore TypeScript function types, including defining function signatures and using optional and default parameters. 912 | 913 | Sure, let's explore functions in TypeScript with some code examples. TypeScript is a statically typed superset of JavaScript, so you'll notice type annotations to specify the types of parameters and return values. This helps catch type-related errors at compile time. 914 | 915 | **Basic Function in TypeScript:** 916 | 917 | ```typescript 918 | function add(a: number, b: number): number { 919 | return a + b; 920 | } 921 | 922 | const result: number = add(5, 3); // 'result' will be 8 923 | ``` 924 | 925 | In this example: 926 | 927 | - `function add(a: number, b: number): number` defines a function named `add` that takes two parameters, `a` and `b`, both of type `number`. It also specifies that the function returns a value of type `number`. 928 | 929 | - The function body adds `a` and `b` and returns the result. 930 | 931 | - We call the `add` function with `add(5, 3)`, and the result is stored in the variable `result`. 932 | 933 | **Function with Default Parameter:** 934 | 935 | You can specify default values for function parameters: 936 | 937 | ```typescript 938 | function greet(name: string = "User"): string { 939 | return `Hello, ${name}!`; 940 | } 941 | 942 | const message: string = greet(); // 'message' will be "Hello, User!" 943 | ``` 944 | 945 | In this example, the `name` parameter has a default value of `"User"`. If you don't provide an argument when calling `greet`, it uses the default value. 946 | 947 | **Function with Rest Parameters:** 948 | 949 | Rest parameters allow you to pass an arbitrary number of arguments as an array: 950 | 951 | ```typescript 952 | function sum(...numbers: number[]): number { 953 | return numbers.reduce((total, num) => total + num, 0); 954 | } 955 | 956 | const total: number = sum(1, 2, 3, 4, 5); // 'total' will be 15 957 | ``` 958 | 959 | The `...numbers` syntax collects all arguments passed to the `sum` function into an array called `numbers`. 960 | 961 | **Function with Callback:** 962 | 963 | You can pass functions as parameters to other functions: 964 | 965 | ```typescript 966 | function calculate(a: number, b: number, operation: (x: number, y: number) => number): number { 967 | return operation(a, b); 968 | } 969 | 970 | const addition = (x: number, y: number) => x + y; 971 | const subtraction = (x: number, y: number) => x - y; 972 | 973 | const result1: number = calculate(5, 3, addition); // 'result1' will be 8 974 | const result2: number = calculate(5, 3, subtraction); // 'result2' will be 2 975 | ``` 976 | 977 | Here, the `calculate` function accepts an `operation` parameter, which is a function that takes two numbers and returns a number. 978 | 979 | **Arrow Functions:** 980 | 981 | Arrow functions provide a concise way to define functions: 982 | 983 | ```typescript 984 | const square = (x: number): number => x * x; 985 | ``` 986 | 987 | - Function signature 988 | 989 | ```js 990 | // function signature 991 | let userInfo1: () => void; 992 | let userInfo2: (name: string) => void; 993 | let userInfo3: (name: string) => string; 994 | 995 | userInfo1 = () => { 996 | console.log('Anisul Islam is 32 years old'); 997 | }; 998 | 999 | userInfo2 = (name: string) => { 1000 | console.log(`${name} is 32 years old`); 1001 | }; 1002 | 1003 | userInfo3 = (name: string): string => { 1004 | return `${name} is 32 years old`; 1005 | }; 1006 | 1007 | userInfo1(); 1008 | userInfo2('Anisul Islam'); 1009 | console.log(userInfo3('Anisul Islam')); 1010 | ``` 1011 | 1012 | - function signature in interface 1013 | 1014 | ```ts 1015 | interface IUserFormatter{ 1016 | formatUser: () => string 1017 | } 1018 | 1019 | class User implements IUserFormatter{ 1020 | constructor(private fullName: string, private age: number){} 1021 | formatUser = () => { 1022 | return `name: ${this.fullName}, age: ${this.age}` 1023 | } 1024 | } 1025 | ``` 1026 | 1027 | ## 2. Intermediate Typescript Topics 1028 | 1029 | ### 2.1 Creating types from types 1030 | 1031 | 1032 | Generics in TypeScript provide a way to create reusable, type-safe functions, classes, and interfaces that work with a variety of data types. They allow you to write code that can operate on values of different types while preserving type information. Generics are denoted using angle brackets (`<>`) and can be used with functions, classes, and interfaces. 1033 | 1034 | Here's an overview of how to use generics in TypeScript: 1035 | 1036 | **1. Generic Functions:** 1037 | 1038 | A generic function can accept arguments of any type while maintaining type safety. You define a generic function by placing a type parameter in angle brackets before the function's parameter list. For example: 1039 | 1040 | ```typescript 1041 | function identity(arg: T): T { 1042 | return arg; 1043 | } 1044 | 1045 | // Usage 1046 | const result1: number = identity(42); // T is inferred as 'number' 1047 | const result2: string = identity("hello"); // T is inferred as 'string' 1048 | ``` 1049 | 1050 | In this example, `T` is a type parameter that represents the type of the argument `arg`. The type of `T` is inferred based on the actual argument passed to the function. 1051 | 1052 | **2. Generic Classes:** 1053 | 1054 | You can create generic classes that work with different types. Here's an example of a generic class that holds a value of type `T`: 1055 | 1056 | ```typescript 1057 | class Box { 1058 | private value: T; 1059 | 1060 | constructor(value: T) { 1061 | this.value = value; 1062 | } 1063 | 1064 | getValue(): T { 1065 | return this.value; 1066 | } 1067 | } 1068 | 1069 | // Usage 1070 | const numberBox = new Box(42); // 'T' is inferred as 'number' 1071 | const stringBox = new Box("hello"); // 'T' is inferred as 'string' 1072 | 1073 | const numValue: number = numberBox.getValue(); 1074 | const strValue: string = stringBox.getValue(); 1075 | ``` 1076 | 1077 | In this case, the class `Box` is generic with a type parameter `T`. It allows you to create instances of `Box` with different types. 1078 | 1079 | **3. Generic Interfaces:** 1080 | 1081 | You can also create generic interfaces that describe the shape of objects with different types: 1082 | 1083 | ```typescript 1084 | interface Pair { 1085 | first: T; 1086 | second: U; 1087 | } 1088 | 1089 | const pair: Pair = { first: 42, second: "hello" }; 1090 | ``` 1091 | 1092 | Here, the `Pair` interface is generic with two type parameters, `T` and `U`, representing the types of the `first` and `second` properties. 1093 | 1094 | Generics in TypeScript provide flexibility and type safety, allowing you to write more reusable and maintainable code. They are particularly useful when working with data structures, utility functions, and libraries that need to work with various types. 1095 | 1096 | ```js 1097 | // make components reusable 1098 | // make components flexible 1099 | // C# and java have this generic feature 1100 | // generic allows us to create own types 1101 | 1102 | // problem without generic 1103 | function printUserInfo(userId: string | number | null){ 1104 | console.log(`user id: ${userId}`) 1105 | } 1106 | printUserInfo("101"); 1107 | printUserInfo(101); 1108 | printUserInfo(null); 1109 | printUserInfo({id: 101}); 1110 | printUserInfo(true); 1111 | 1112 | const displayValue1 = (x) => { 1113 | return x; 1114 | }; 1115 | // no auto suggestion for any type 1116 | // displayValue1(20).; 1117 | 1118 | const displayValue2 = (x: T): T => { 1119 | return x; 1120 | }; 1121 | // now we will have auto suggestion 1122 | // displayValue2(20). 1123 | 1124 | const addBefore = (numbers: T1[], x: T1) => { 1125 | return [x, ...numbers]; 1126 | }; 1127 | 1128 | let numbers = [20, 30, 40]; 1129 | console.log(addBefore(numbers, 10)); 1130 | 1131 | let countries = ['bangladesh', 'pakistan', 'India']; 1132 | console.log(addBefore(countries, 'Australia')); 1133 | 1134 | // Another example 1135 | const printUserInfo = (userId: X, userAge: Y) => { 1136 | console.log(`ID : ${userId}, Age: ${userAge}`); 1137 | }; 1138 | printUserInfo('101', 32); 1139 | printUserInfo(101, 32); 1140 | printUserInfo(101, '32'); 1141 | printUserInfo('101', '32'); 1142 | ``` 1143 | 1144 | #### keyof type Example 1145 | 1146 | The `keyof` keyword in TypeScript is used to create a union type of all the keys of an object type. It allows you to work with object keys in a type-safe way. Here's an example of how to use `keyof` in TypeScript: 1147 | 1148 | ```typescript 1149 | // Define an object type 1150 | type Person = { 1151 | name: string; 1152 | age: number; 1153 | address: string; 1154 | }; 1155 | 1156 | // Use 'keyof' to create a union type of all keys in the 'Person' type 1157 | type PersonKeys = keyof Person; 1158 | 1159 | // Usage example 1160 | const person: Person = { 1161 | name: "Alice", 1162 | age: 30, 1163 | address: "123 Main St", 1164 | }; 1165 | 1166 | // Accessing properties using 'keyof' and dot notation 1167 | const nameKey: PersonKeys = "name"; // Valid 1168 | const ageKey: PersonKeys = "age"; // Valid 1169 | const addressKey: PersonKeys = "address"; // Valid 1170 | 1171 | // The following will result in a compile error: 1172 | // const invalidKey: PersonKeys = "email"; // Error: Type '"email"' is not assignable to type 'keyof Person'. 1173 | ``` 1174 | 1175 | In this example: 1176 | 1177 | - We define an object type `Person` with properties `name`, `age`, and `address`. 1178 | 1179 | - We use `keyof Person` to create a type `PersonKeys`, which is a union type of all keys in the `Person` type. In this case, `PersonKeys` is equivalent to `"name" | "age" | "address"`. 1180 | 1181 | - We demonstrate how to use `PersonKeys` to declare variables like `nameKey`, `ageKey`, and `addressKey`, which can only be assigned keys that exist in the `Person` type. 1182 | 1183 | - If you attempt to assign a value that is not a valid key in the `Person` type, TypeScript will raise a compile-time error, ensuring type safety. 1184 | 1185 | The `keyof` type operator is useful when you want to perform operations on object keys dynamically or when you want to enforce type safety when working with object properties. It is commonly used in scenarios where you want to write code that can work with various object structures without compromising type safety. 1186 | 1187 | #### typeof type Example - 1188 | 1189 | ```js 1190 | let firstName: string; 1191 | let lastName: typeof firstName; 1192 | ``` 1193 | 1194 | #### Conditional type Example - 1195 | 1196 | ```js 1197 | interface Animal { 1198 | live(): void; 1199 | } 1200 | interface Dog extends Animal { 1201 | woof(): void; 1202 | } 1203 | 1204 | type Example1 = Dog extends Animal ? number : string; 1205 | 1206 | type Example1 = number 1207 | 1208 | type Example2 = RegExp extends Animal ? number : string; 1209 | 1210 | type Example2 = string 1211 | ``` 1212 | 1213 | ### 2.2 Narrowing 1214 | 1215 | Narrowing in TypeScript refers to the process of refining the type of a variable or expression within a certain code block based on conditional checks. It allows you to make your code more type-safe by reducing the range of possible types for a variable when certain conditions are met. TypeScript's control flow analysis can automatically narrow the types of variables based on the context. 1216 | 1217 | Here's an example of narrowing in TypeScript: 1218 | 1219 | ```typescript 1220 | function printLength(input: string | number) { 1221 | if (typeof input === "string") { 1222 | // Inside this block, 'input' is narrowed to type 'string' 1223 | console.log(`Length of string: ${input.length}`); 1224 | } else { 1225 | // Inside this block, 'input' is narrowed to type 'number' 1226 | console.log(`Value of number: ${input}`); 1227 | } 1228 | } 1229 | 1230 | printLength("Hello, TypeScript!"); // Output: "Length of string: 18" 1231 | printLength(42); // Output: "Value of number: 42" 1232 | ``` 1233 | 1234 | In this example: 1235 | 1236 | - The `printLength` function takes an argument `input`, which can be either a `string` or a `number`. 1237 | 1238 | - Inside the function, we use a conditional statement (`if`) to check the type of `input` using `typeof`. When TypeScript sees this check, it narrows the type of `input` within each branch of the conditional: 1239 | 1240 | - In the first branch, where `typeof input === "string"`, TypeScript narrows `input` to type `string`. This allows us to safely access the `length` property, which is specific to strings. 1241 | 1242 | - In the second branch, where `typeof input !== "string"`, TypeScript narrows `input` to type `number`. 1243 | 1244 | This type narrowing ensures that we can only access properties or perform operations that are valid for the narrowed type within each branch. It helps catch type-related errors at compile time and provides better type safety. 1245 | 1246 | TypeScript's control flow analysis is especially useful when working with union types, where you need to handle different types within the same function or code block while maintaining type safety. 1247 | 1248 | ### 2.3 Type guards Example 1249 | 1250 | - type guards with typeof 1251 | 1252 | ```js 1253 | // type guards with typeof 1254 | // typeof variable === string/number/boolean/symbol/undefined/object/function 1255 | const printAllTodos = (todos: string[] | null) => { 1256 | if (typeof todos === 'object') { 1257 | todos.map((todo) => console.log(todo)); 1258 | } else { 1259 | console.log('todos are empty'); 1260 | } 1261 | }; 1262 | ``` 1263 | 1264 | #### Truthiness narrowing Example 1265 | 1266 | ```js 1267 | // false -> 0,NaN,"" (the empty string), 0n (the bigint version of zero), null, undefined 1268 | const printAllTodos = (todos: string[] | null) => { 1269 | if (todos) { 1270 | todos.map((todo) => console.log(todo)); 1271 | } else { 1272 | console.log('todos are empty'); 1273 | } 1274 | }; 1275 | 1276 | const todos1 = null; 1277 | const todos2 = ['todo1', 'todo2', 'todo3']; 1278 | printAllTodos(todos1); 1279 | printAllTodos(todos2); 1280 | ``` 1281 | 1282 | #### Equality narrowing Example 1283 | 1284 | ```js 1285 | // == , ===, !=, !=== helps to narrow types 1286 | ``` 1287 | 1288 | ### 2.4 DOM Manipulation with typescript 1289 | 1290 | - Example1 1291 | 1292 | ```html 1293 | 1294 | 1295 | 1296 | 1297 |

1298 | 1299 | 1300 | ``` 1301 | 1302 | ```js 1303 | const number1 = document.querySelector(".input1") as HTMLInputElement; 1304 | const number2 = document.querySelector(".input2") as HTMLInputElement; 1305 | const addButton = document.querySelector("button")!; 1306 | const result = document.querySelector("p")!; 1307 | 1308 | addButton?.addEventListener("click", () => { 1309 | const sum = Number(number1.value) + Number(number2.value); 1310 | result.textContent = `The result is ${sum}`; 1311 | }); 1312 | ``` 1313 | 1314 | - Example2 1315 | 1316 | ```html 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | Document 1324 | 1325 | 1326 | 1327 |

welcome

1328 |
1329 |
1330 | 1331 | 1332 |
1333 |
1334 | 1335 | 1336 |
1337 | 1338 |
1339 | 1340 | 1344 |
1345 | 1346 |
1347 | 1348 | 1349 |
1350 | 1351 |
1352 | 1353 | 1354 | 1355 | ``` 1356 | 1357 | ```ts 1358 | const form = document.querySelector('.user-form') as HTMLFormElement; 1359 | console.log(form); 1360 | 1361 | const userNameInput = document.querySelector('#username') as HTMLInputElement; 1362 | console.log(userNameInput); 1363 | 1364 | const userEmailInput = document.querySelector('#useremail') as HTMLInputElement; 1365 | console.log(userEmailInput); 1366 | 1367 | const userCountrySelect = document.querySelector( 1368 | '#country' 1369 | ) as HTMLSelectElement; 1370 | console.log(userCountrySelect); 1371 | 1372 | const userFeedback = document.querySelector('#feedback') as HTMLTextAreaElement; 1373 | console.log(userFeedback); 1374 | 1375 | form.addEventListener('submit', (e: Event) => { 1376 | e.preventDefault(); 1377 | let userData = { 1378 | userName: userNameInput.value, 1379 | userEmail: userEmailInput.value, 1380 | userCountry: userCountrySelect.value, 1381 | userFeedback: userFeedback.value, 1382 | }; 1383 | console.log(userData); 1384 | }); 1385 | ``` -------------------------------------------------------------------------------- /images/access-modifiers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anisul-Islam/typescript-documentation/05baea476f0b578459eb746ce980c3e24ca5c8c4/images/access-modifiers.png --------------------------------------------------------------------------------