├── 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 |
4 |
5 |
6 | TypeScript Fundamentals in One Place
7 |
8 |
9 |
10 | [](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 |
--------------------------------------------------------------------------------