├── images ├── typescript.png └── typescript-diagram.png └── README.md /images/typescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haddadzineddine/TypeScript-Fundamentals-in-One-Place/HEAD/images/typescript.png -------------------------------------------------------------------------------- /images/typescript-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haddadzineddine/TypeScript-Fundamentals-in-One-Place/HEAD/images/typescript-diagram.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | TypeScript Fundamentals in One Place 4 |
5 |
6 | TypeScript Fundamentals in One Place 7 |

8 |

9 | 10 | [![Follow me](https://img.shields.io/twitter/follow/zineddine_dz_21.svg?style=for-the-badge)](https://twitter.com/zineddine_dz_21) 11 | 12 | ## Table of Contents 13 | 14 | 1. **[Introduction](#1-introduction)** 15 | 2. **[Built-in Types](#2-built-in-types)** 16 | 3. **[Object Oriented Programming](#3-object-oriented-programming)** 17 | 4. **[Generic](#4-generics)** 18 | 5. **[Decorators](#4-decorators)** 19 | 20 | ## 1. Introduction 21 | 22 | Programming language divide into two categories : 23 | 24 | - `Statically typed` 25 | - `Dynamically typed` 26 | 27 | in `Statically-typed` languages (C, Java, C#, ... ), the type of variable is set at the compile-time and cannot change later. 28 | 29 | in `Dynamically-typed` languages (PHP, JavaScript, Python, ... ), the type of variable is determined at the run-time and can change later. 30 | 31 | `TypeScript` is a programming language build on top of `JavaScript` ( essentially JavaScript with static typing and some additional features ) , so before we star make sure that you are familiar with this concepts in javascript: 32 | 33 | - Variables 34 | - Arrays 35 | - Objects 36 | - Functions 37 | - Arrow Functions 38 | - Destructuring 39 | - ... 40 | 41 | ## 2. Built-in Types 42 | 43 | as we know `JavaScript` has built-in types like : 44 | 45 | - number 46 | - string 47 | - boolean 48 | - array 49 | - object 50 | - undefined 51 | - null 52 | 53 | So `TypeScript` extend this list and introduce some new built-in types such as : 54 | 55 | - any 56 | - unknown 57 | - never 58 | - enum 59 | - tuple 60 | 61 | `1- The Any Type :` when you declare a variable and don't initialize it , the typescript compiler will assume that variable is type of `any` which means you can assign any type of data into it , here is an example : 62 | 63 | ```typescript 64 | let anyType; // let anyType: any 65 | 66 | anyType = 12; 67 | 68 | console.log(typeof anyType); // output: number 69 | 70 | anyType = "Random string"; 71 | 72 | console.log(typeof anyType); // output: string 73 | ``` 74 | 75 | `Note :` To declare `variables, functions` you just need to follow this syntax : 76 | 77 | ```typescript 78 | let numberType: number = 12; 79 | let numberType: string = 12; 80 | 81 | function taxe(income: number): number { 82 | return income * 0.2; 83 | } 84 | ``` 85 | 86 | `2- Arrays :` 87 | 88 | ```typescript 89 | let numbers = [1, 2, 3]; // let numbers: number[] = [1, 2, 3] 90 | 91 | let anyTypes = []; // let anyTypes: any[] 92 | 93 | anyType[0] = 100; 94 | anyType[0] = "r_string"; 95 | 96 | let names = ["ahmed", "zineddine"]; // let names: string[] = ["ahmed", "zineddine"] 97 | ``` 98 | 99 | `3- Tuples :` A tuple is a typed array with a pre-defined length and types for each index. 100 | 101 | ```typescript 102 | let employee: [number, string] = [1, "Steve"]; 103 | ``` 104 | 105 | `4- Enums :` 106 | 107 | ```typescript 108 | // const small = 1; 109 | // const medium = 1; 110 | // const large = 1; 111 | 112 | const enum Size { 113 | Small = 1, 114 | medium, 115 | large, 116 | } 117 | 118 | let mySize: Size = Size.Small; 119 | 120 | console.log(mySize); // output : 1 121 | ``` 122 | 123 | `4- Objects :` 124 | 125 | ```typescript 126 | /* 127 | let employee: { 128 | id:number, 129 | name:string 130 | } = { 131 | id:1, 132 | name:'zineddine' 133 | } 134 | */ 135 | 136 | let employee = { 137 | id: 1, 138 | name: "zineddine", 139 | }; 140 | 141 | let user: { 142 | readonly id: number; 143 | name: string; 144 | pseudo?: string; 145 | retire: (date: Date) => void; // function declaration 146 | } = { 147 | id: 1, 148 | name: "zineddine", 149 | retire: (date: Date) => { 150 | console.log(date); 151 | }, 152 | }; 153 | 154 | user.id = 10; // Cannot assign to 'id' because it is a read-only property 155 | ``` 156 | 157 | `5- Type Aliases :` 158 | 159 | ```typescript 160 | type User = { 161 | readonly id: number; 162 | name: string; 163 | pseudo?: string; 164 | retire: (date: Date) => void; // function declaration 165 | }; 166 | 167 | let user: User = { 168 | id: 1, 169 | name: "zineddine", 170 | retire: (date: Date) => { 171 | console.log(date); 172 | }, 173 | }; 174 | ``` 175 | 176 | `6- Union Types :` 177 | 178 | ```typescript 179 | function kgToLbs(kg: number | string): number { 180 | // Narrowing 181 | if (typeof kg === "string") { 182 | return parseFloat(kg) * 2.2046; 183 | } 184 | 185 | return kg * 2.2046; 186 | } 187 | ``` 188 | 189 | `7- Intersection Types :` 190 | 191 | ```typescript 192 | // make no sense right ? 193 | let weight: number & string; 194 | 195 | // let see a realistic example 196 | 197 | type draggable = { 198 | drag: () => void; 199 | }; 200 | 201 | type resizable = { 202 | resize: () => void; 203 | }; 204 | 205 | let UIWidget: draggable & resizable; 206 | 207 | UIWidget = { 208 | drag: () => {}, 209 | resize: () => {}, 210 | }; 211 | ``` 212 | 213 | `8- Literal Types :` 214 | 215 | ```typescript 216 | // let quantity: 5 | 100; 217 | 218 | type Quantity = 50 | 100; 219 | let quantity: Quantity; 220 | 221 | quantity = 5; // Type '5' is not assignable to type 'Quantity' 222 | 223 | type Metric = "m" | "cm" | "mm"; 224 | ``` 225 | 226 | `9- Nullable Types :` 227 | 228 | ```typescript 229 | function greeting(name: string | null | undefined) { 230 | if (name) { 231 | return `Hello, ${name}`; 232 | } 233 | return "Hello, World"; 234 | } 235 | 236 | greeting("John"); 237 | greeting(null); 238 | greeting(undefined); 239 | ``` 240 | 241 | `10- Optional Chaining :` 242 | 243 | ```typescript 244 | type User = { 245 | id: number; 246 | birthday?: Date; 247 | }; 248 | 249 | function getUser(id: number): User | null | undefined { 250 | if (id === 1) { 251 | return { 252 | id, 253 | birthday: new Date("2000-01-01"), 254 | }; 255 | } 256 | 257 | return null; 258 | } 259 | 260 | getUser(0); // output null 261 | 262 | getUser(1); // output { id: 1, birthday: Date } 263 | 264 | // optional property access operator 265 | getUser(1)?.birthday?.getFullYear(); // output 2000 266 | 267 | // optional element access operator 268 | 269 | let employees: string[] | null = null; 270 | employees?.[0]; 271 | 272 | // optional function call operator 273 | 274 | let log: any = null; 275 | 276 | log?.("hello"); // return undefined 277 | ``` 278 | 279 | `11- Nullish Coalescing Operator :` 280 | 281 | ```typescript 282 | let speed: number | null = null; 283 | 284 | let ride = { 285 | // Falsy values ( false, 0, '', null, undefined ) 286 | // speed: speed || 30, if speed is falsy, set it to 30 , but 0 is falsy 287 | // speed: speed != null ? speed : 30, 288 | speed: speed ?? 30, 289 | }; 290 | ``` 291 | 292 | `12- Type Assertions :` 293 | 294 | ```typescript 295 | let phone = document.getElementById("phone"); 296 | 297 | phone.value; // Property 'value' does not exist on type 'HTMLElement' 298 | 299 | // let email = document.getElementById('email'); 300 | 301 | let email = document.getElementById("email") as HTMLInputElement; 302 | 303 | email.value; 304 | ``` 305 | 306 | `13- The Unknown Type :` 307 | 308 | ```typescript 309 | function render(document: any) { 310 | // no compile error , but runtime error 311 | document.whatEver(); 312 | } 313 | 314 | function render(document: unknown) { 315 | /* 316 | compile error, now the compîler forces us to check the type of the argument before using it 317 | 318 | */ 319 | 320 | document.whatEver(); 321 | 322 | if (document instanceof String) { 323 | document.toLocaleLowerCase(); 324 | } 325 | } 326 | ``` 327 | 328 | `13- The Never Type :` 329 | 330 | ```typescript 331 | function reject(message: string): never { 332 | throw new Error(message); 333 | } 334 | 335 | function processEvent(): never { 336 | while (true) { 337 | // ... 338 | } 339 | } 340 | 341 | processEvent(); 342 | 343 | /* 344 | 345 | => this code will never be executed , but the compiler don't tell us , so we have to use the `never` type. 346 | 347 | => now the compiler will tell us that the function will never return : Unreachable code detected. 348 | 349 | */ 350 | 351 | console.log("Hello World!"); 352 | ``` 353 | 354 | ## 3. Object-oriented Programming 355 | 356 | As we know `JavaScript` does'nt have the concept of `classes` like other programming languages such as ( PHP, Java, C++, C# ... ). 357 | 358 | with `ES6` you can defined `classes` but it's just a syntactic sugar for creating `constructor function` and `prototypal inheritance`. 359 | 360 | Let's see `OOP` in `TypeScript` : 361 | 362 | `1- Creating Classes and objects :` 363 | 364 | ```typescript 365 | class Account { 366 | id: number; 367 | owner: string; 368 | balance: number; 369 | 370 | constructor(id: number, owner: string, balance: number) { 371 | this.id = id; 372 | this.owner = owner; 373 | this.balance = balance; 374 | } 375 | 376 | deposit(amount: number): void { 377 | if (amount > 0) { 378 | this.balance += amount; 379 | } 380 | 381 | throw new Error("Invalid amount"); 382 | } 383 | } 384 | 385 | let account = new Account(1, "zineddine", 100); 386 | 387 | account.deposit(100); 388 | 389 | console.log(typeof account); // object 390 | console.log(account instanceof Account); // true 391 | 392 | /* 393 | 394 | always make sure to use instanceof property to check if 395 | an object is an instance of a class 396 | 397 | */ 398 | ``` 399 | 400 | `Note :` You can't use the `function` keyword inside a `class` to declare a `function`, use it only when you declare a `stand-alone function`. 401 | 402 | `2- Read-only and Optional Properties :` 403 | 404 | ```typescript 405 | class User { 406 | readonly id: number; 407 | name: string; 408 | email: string; 409 | nickname?: string; // optional property 410 | 411 | constructor(id: number, name: string, email: string) { 412 | this.id = id; 413 | this.name = name; 414 | this.email = email; 415 | } 416 | } 417 | 418 | let user = new User(1, "zineddine", "hz_haddad@esi.dz"); 419 | user.id = 12; // Cannot assign to 'id' because it is a read-only property 420 | ``` 421 | 422 | `3- Access Control Keywords :` 423 | 424 | ```typescript 425 | class Account { 426 | /* 427 | public # by default 428 | private 429 | protected 430 | */ 431 | 432 | id: number; 433 | private _balance: number; 434 | 435 | constructor(id: number, balance: number) { 436 | this.id = id; 437 | this._balance = balance; 438 | } 439 | 440 | deposit(amount: number): void { 441 | if (amount > 0) { 442 | // assume we want also to log the transaction 443 | this._balance += amount; 444 | } 445 | 446 | throw new Error("Invalid amount"); 447 | } 448 | 449 | private calculateTax(amount: number): number { 450 | return amount * 0.1; 451 | } 452 | 453 | getBalance(): number { 454 | return this._balance; 455 | } 456 | } 457 | 458 | let account = new Account(1, 100); 459 | account._balance -= 50; // Property '_balance' is private and only accessible within class 'Account' 460 | ``` 461 | 462 | `4- Parameter Properties and Getters & Setters :` 463 | 464 | ```typescript 465 | class Account { 466 | nickname?: string; // optional property 467 | 468 | constructor( 469 | public readonly id: number, 470 | public owner: string, 471 | private _balance: number 472 | ) {} 473 | 474 | get balance(): number { 475 | return this._balance; 476 | } 477 | 478 | set balance(value: number) { 479 | if (value < 0) { 480 | throw new Error("Balance cannot be negative"); 481 | } 482 | this._balance = value; 483 | } 484 | } 485 | 486 | let account = new Account(1, "zineddine", 100); 487 | 488 | console.log(account.balance); // 100 489 | account.balance = -100; // throws error 490 | account.balance = 100; // OK 491 | ``` 492 | 493 | `5- Index Signatures :` Index Signatures are just a fancy name for `dynamic properties` 494 | 495 | ```typescript 496 | class NameByNumber { 497 | // index signature property 498 | [name: string]: number; 499 | } 500 | 501 | let nameByNumber = new NameByNumber(); 502 | 503 | nameByNumber.John = 1; 504 | // nameByNumber.['John'] = 1; 505 | // nameByNumber.John = '1'; Type 'string' is not assignable to type 'number' 506 | nameByNumber.Jane = 2; 507 | nameByNumber.Bob = 3; 508 | 509 | console.log(nameByNumber.John); // 1 510 | ``` 511 | 512 | `6- Static Members :` 513 | 514 | ```typescript 515 | class Ride { 516 | private static _activeRides: number = 0; 517 | 518 | start() { 519 | Ride._activeRides++; 520 | } 521 | 522 | end() { 523 | Ride._activeRides--; 524 | } 525 | 526 | static get activeRides() { 527 | return Ride._activeRides; 528 | } 529 | } 530 | 531 | let ride1 = new Ride(); 532 | let ride2 = new Ride(); 533 | 534 | ride1.start(); 535 | ride2.start(); 536 | 537 | console.log(Ride.activeRides); // 2 538 | ``` 539 | 540 | `7- Inheritance and Methods Overriding :` 541 | 542 | ```typescript 543 | class Person { 544 | constructor(public firstName: string, public lastName: string) {} 545 | 546 | get fullName() { 547 | return this.firstName + " " + this.lastName; 548 | } 549 | 550 | walk() { 551 | console.log("Walking"); 552 | } 553 | } 554 | 555 | class Student extends Person { 556 | constructor(firstName: string, lastName: string, public id: number) { 557 | super(firstName, lastName); 558 | } 559 | 560 | override walk() { 561 | super.walk(); 562 | console.log("Walking on the stairs"); 563 | } 564 | 565 | override get fullName() { 566 | return "Student : " + super.fullName; 567 | } 568 | } 569 | 570 | let student = new Student("John", "Doe", 123); 571 | 572 | console.log(student.fullName); 573 | student.walk(); 574 | 575 | /* 576 | 577 | Walking 578 | Walking on the stairs 579 | 580 | */ 581 | 582 | console.log(student instanceof Person); // true 583 | ``` 584 | 585 | `8- Polymorphism :` 586 | 587 | ```typescript 588 | // parent class , base class , super class 589 | class Person { 590 | protected steps: number = 0; 591 | 592 | constructor(public firstName: string, public lastName: string) {} 593 | 594 | get fullName() { 595 | return this.firstName + " " + this.lastName; 596 | } 597 | } 598 | 599 | // child class , sub class , derived class 600 | class Student extends Person { 601 | constructor(firstName: string, lastName: string, public id: number) { 602 | super(firstName, lastName); 603 | } 604 | 605 | override get fullName() { 606 | return "Student : " + super.fullName; 607 | } 608 | } 609 | 610 | class Teacher extends Person { 611 | constructor(firstName: string, lastName: string, public id: number) { 612 | super(firstName, lastName); 613 | } 614 | 615 | override get fullName() { 616 | return "Teacher : " + super.fullName; 617 | } 618 | } 619 | 620 | function printName(persons: Person[]) { 621 | for (let person of persons) { 622 | console.log(person.fullName); 623 | } 624 | } 625 | 626 | printName([ 627 | new Person("John", "Doe"), 628 | new Student("Jane", "Doe", 123), 629 | new Teacher("John", "Doe", 123), 630 | ]); 631 | 632 | /* 633 | 634 | John Doe 635 | Student : Jane Doe 636 | Teacher : John Doe 637 | 638 | */ 639 | ``` 640 | 641 | `9- Abstract Classes :` 642 | 643 | ```typescript 644 | abstract class Shape { 645 | constructor(public color: string) {} 646 | 647 | abstract render(): void; 648 | } 649 | 650 | class Circle extends Shape { 651 | constructor(public radius: number, color: string) { 652 | super(color); 653 | } 654 | 655 | override render(): void { 656 | console.log("Circle"); 657 | } 658 | } 659 | 660 | let shape = new Shape("red"); // Cannot create an instance of an abstract class 661 | ``` 662 | 663 | `10- Interfaces :` 664 | 665 | ```typescript 666 | interface Calender { 667 | name: string; 668 | addEvent(event: string): void; 669 | removeEvent(event: string): void; 670 | } 671 | 672 | interface CloudCalender extends Calender { 673 | sync(): void; 674 | } 675 | 676 | class GoogleCalender implements CloudCalendar { 677 | constructor(public name: string) {} 678 | 679 | addEvent(event: string): void { 680 | console.log(`Adding ${event} to GoogleCalendar`); 681 | } 682 | removeEvent(event: string): void { 683 | console.log(`Removing ${event} from GoogleCalendar`); 684 | } 685 | sync(): void { 686 | console.log("Syncing GoogleCalendar"); 687 | } 688 | } 689 | ``` 690 | 691 | `Note :` In `TypeScript`, `interfaces` and `type aliases` can be used interchangeably. 692 | Both can be used to describe the shape of an object 693 | 694 | ```typescript 695 | interface Person { 696 | name: string; 697 | } 698 | 699 | let person: Person = { 700 | name: "Zineddine", 701 | }; 702 | 703 | type User = { 704 | name: string; 705 | }; 706 | 707 | let user: User = { 708 | name: "Zineddine", 709 | }; 710 | ``` 711 | 712 | ## 4. Generics 713 | 714 | `1- Generic Classes and The Keyof Operator :` 715 | 716 | ```typescript 717 | class KeyValuePair { 718 | constructor(public key: K, public value: V) {} 719 | } 720 | 721 | let kvp = new KeyValuePair("name", 10); 722 | /* 723 | 724 | you can also use this syntax : 725 | let kvp = new KeyValuePair('name', 10); 726 | the compiler will refer the type of the key and value for us 727 | 728 | */ 729 | ``` 730 | 731 | `2- Generic Functions :` 732 | 733 | ```typescript 734 | class ArrayUtils { 735 | static wrapInArray(value: T) { 736 | return Array.isArray(value) ? value : [value]; 737 | } 738 | } 739 | 740 | let numbers = ArrayUtils.wrapInArray(1); 741 | let strings = ArrayUtils.wrapInArray("hello"); 742 | 743 | console.log(numbers); // [1] 744 | console.log(strings); // ["hello"] 745 | ``` 746 | 747 | `3- Generic Interfaces :` 748 | 749 | ```typescript 750 | interface Result { 751 | data: T | null; 752 | error: string | null; 753 | } 754 | 755 | interface User { 756 | userName: string; 757 | } 758 | 759 | interface Product { 760 | productName: string; 761 | } 762 | 763 | function fetch(url: string): Result { 764 | return { 765 | data: null, 766 | error: null, 767 | }; 768 | } 769 | 770 | fetch("url").data?.userName; 771 | fetch("url").data?.productName; 772 | ``` 773 | 774 | `4- Generic Constraints :` 775 | 776 | ```typescript 777 | class Person { 778 | constructor(public name: string) {} 779 | } 780 | 781 | class Student extends Person {} 782 | 783 | function echo(arg: T): T { 784 | return arg; 785 | } 786 | 787 | echo(new Person("John")); 788 | echo(new Student("Zineddine")); 789 | echo(10); // Argument of type 'number' is not assignable to parameter of type 'Person' 790 | ``` 791 | 792 | `5- Extending Generic Classes :` 793 | 794 | ```typescript 795 | class Product { 796 | constructor(public name: string, public price: number) {} 797 | } 798 | 799 | class Store { 800 | protected _objects: T[] = []; 801 | 802 | addObject(object: T) { 803 | this._objects.push(object); 804 | } 805 | 806 | find(property: keyof T, value: unknown): T[] { 807 | return this._objects.filter((o) => o[property] === value); 808 | } 809 | } 810 | 811 | // pass on the generic type parameter 812 | class CompressibleStore extends Store { 813 | compress() { 814 | this._objects.forEach((object) => { 815 | console.log(object); 816 | }); 817 | } 818 | } 819 | 820 | let compressibleStore = new CompressibleStore(); 821 | compressibleStore.addObject(new Product("Product 1", 100)); 822 | 823 | compressibleStore.compress(); // Product { name: 'Product 1', price: 100 } 824 | 825 | // Restrictions the generic type parameter 826 | class SearchableStore extends Store { 827 | search(searchTerm: string) { 828 | this._objects.find((object) => { 829 | return object.name === searchTerm; 830 | }); 831 | } 832 | } 833 | 834 | // Fix the generic type parameter 835 | class ProductStore extends Store {} 836 | 837 | let store = new Store(); 838 | 839 | store.addObject(new Product("Product 1", 100)); 840 | store.addObject(new Product("Product 2", 200)); 841 | 842 | store.find("name", "Product 1"); // [Product { name: 'Product 1', price: 100 }] 843 | 844 | store.find("name", "Product 3"); // [] 845 | 846 | store.find("nonExistingProperty", "Product 3"); // Argument of type '"nonExistingProperty"' is not assignable to parameter of type 'keyof Product' 847 | ``` 848 | 849 | `6- Type mapping :` 850 | 851 | ```typescript 852 | interface Product { 853 | name: string; 854 | price: number; 855 | } 856 | 857 | type ReadOnly = { 858 | readonly [K in keyof T]: T[K]; 859 | }; 860 | 861 | type Optional = { 862 | [K in keyof T]?: T[K]; 863 | }; 864 | 865 | type Nullable = { 866 | [K in keyof T]: T[K] | null; 867 | }; 868 | ``` 869 | 870 | ## 4. Decorators 871 | 872 | A decorator is just a **function** that gets called by the javaScript runtime, in that function we have a chance to modify a class and its members. 873 | 874 | Decorators are often used in frameworks ( eg angular , vue , nest js ... ) to chance and enhace classes and how they behave. 875 | 876 | We can apply decorators on classes, properties, methodes, parameters, and accessors ( Getters and Setters ). 877 | 878 | We can apply more than one decorator to class or its memebers. Multiple decorators are applied in the reverse order. 879 | 880 | To use decorators, we have to enable **experimentalDecorators** setting in tsconfig. 881 | 882 | `1- Class decorators :` 883 | 884 | ```typescript 885 | function Component(constructor: Function) { 886 | // Here we have a chance to modify members of // the target class. constructor.prototype.uniqueId = Date.now(); 887 | } 888 | 889 | @Component 890 | class ProfileComponent { } 891 | ``` 892 | 893 | `2- Parameterized decorators :` 894 | 895 | ```typescript 896 | function Component(value: number) { 897 | return (constructor: Function) => { 898 | // Here we have a chance to modify members of 899 | // the target class. 900 | constructor.prototype.uniqueId = Date.now(); 901 | }; 902 | } 903 | @Component(1) 904 | class ProfileComponent {} 905 | ``` 906 | 907 | `3- Decorator composition :` 908 | 909 | ```typescript 910 | // Multiple decorators are applied in reverse order. 911 | // Pipe followed by Component. 912 | @Component 913 | @Pipe 914 | class ProfileComponent {} 915 | ``` 916 | 917 | `4- Method decorators :` 918 | 919 | ```typescript 920 | function Log(target: any, methodName: string, descriptor: PropertyDescriptor) { 921 | // We get a reference to the original method 922 | const original = descriptor.value as Function; 923 | // Then, we redefine the method 924 | descriptor.value = function(...args: any) { 925 | // We have a chance to do something first 926 | console.log('Before'); 927 | // Then, we call the original method 928 | original.call(this, ...args; 929 | // And we have a chance to do something after 930 | console.log('After'); 931 | } 932 | } 933 | 934 | class Person { 935 | @Log 936 | say(message: string) {} 937 | } 938 | ``` 939 | 940 | `4- Accessor decorators :` 941 | 942 | ```typescript 943 | function Capitalize(target: any, methodName: string, descriptor: PropertyDescriptor) { 944 | const original = descriptor.get; 945 | descriptor.get = function() { 946 | const result = original.call(this); 947 | return 'newResult'; 948 | } 949 | } 950 | 951 | class Person { 952 | @Capitalize 953 | get fullName() {} 954 | } 955 | ``` 956 | 957 | `5- Property decorators :` 958 | 959 | ```typescript 960 | function MinLength(length: number) { 961 | return (target: any, propertyName: string) => { 962 | // We use this variable to hold the value behind the 963 | // target property. 964 | let value: string; 965 | // We create a descriptor for the target property. 966 | const descriptor: PropertyDescriptor = { 967 | // We're defining the setter for the target property. 968 | set(newValue: string) { 969 | if (newValue.length < length) throw new Error(); 970 | value = newValue; 971 | } 972 | } 973 | // And finally, we redefine the property. 974 | Object.defineProperty(target, propertyName, descriptor); 975 | } 976 | } 977 | class User { 978 | @MinLength(4) password: string; 979 | } 980 | ``` 981 | 982 | --------------------------------------------------------------------------------