├── .gitignore
├── LICENSE
├── README.md
└── cheatsheet
├── assets
├── intro.png
└── logo.png
├── ts.md
└── ts.pdf
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vscode
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Leonardo Folgoni
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ts-extended-cheatsheet
2 | An extended cheatsheet about TypeScript
3 |
4 | ## DISCLAIMER
5 |
6 | This is not a full, well-curated documentation about TypeScript. These are the notes I took following various sources while learning TypeScript. I'm Italian, so sorry for the not so perfect English.
7 |
8 | ## Cheatsheet
9 |
10 | MD: [HERE](https://github.com/f0lg0/ts-extended-cheatsheet/blob/master/cheatsheet/ts.md)
11 |
12 | PDF: [HERE](https://github.com/f0lg0/ts-extended-cheatsheet/blob/master/cheatsheet/ts.pdf)
13 |
14 |
--------------------------------------------------------------------------------
/cheatsheet/assets/intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f0lg0/ts-extended-cheatsheet/e66c1aaea0558b77d4246f81348aa2e2d94cd63b/cheatsheet/assets/intro.png
--------------------------------------------------------------------------------
/cheatsheet/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f0lg0/ts-extended-cheatsheet/e66c1aaea0558b77d4246f81348aa2e2d94cd63b/cheatsheet/assets/logo.png
--------------------------------------------------------------------------------
/cheatsheet/ts.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
TypeScript
6 |
7 | Table of Contents
8 | =================
9 |
10 | * [TypeScript](#typescript)
11 | * [Introduction](#introduction)
12 | * [What is TypeScript?](#what-is-typescript)
13 | * [What does it add?](#what-does-it-add)
14 | * [Why TypeScript?](#why-typescript)
15 | * [Requirements](#requirements)
16 | * [Install typescript](#install-typescript)
17 | * [Overview](#overview)
18 | * [Basics](#basics)
19 | * [Core Types](#core-types)
20 | * [Using types](#using-types)
21 | * [Type Inference](#type-inference)
22 | * [Object types](#object-types)
23 | * [Array types](#array-types)
24 | * [More Types](#more-types)
25 | * [Tuples](#tuples)
26 | * [Enum](#enum)
27 | * [Union Types](#union-types)
28 | * [Literal Types](#literal-types)
29 | * [Type Aliases](#type-aliases)
30 | * [Function Return Types](#function-return-types)
31 | * [Function as Types](#function-as-types)
32 | * [Unknown Type](#unknwon-type)
33 | * [Never Type](#never-type)
34 | * [TS Compiler](#ts-compiler)
35 | * [Command Options](#command-options)
36 | * [Flags in tsconfig](#flags-in-tsconfig)
37 | * [Classes and Interfaces](#classes-and-interfaces)
38 | * [Classes](#classes)
39 | * [Private Modifiers](#private-modifiers)
40 | * [Public Modifiers](#public-modifiers)
41 | * [Shorthand Initialization](#shorthand-initialization)
42 | * [Read-only Modifier](#read-only-modifier)
43 | * [Inheritance](#inheritance)
44 | * [Getters and Setters](#getters-and-setters)
45 | * [Static Properties and Methods](#static-properties-and-methods)
46 | * [Abstract Classes](#absract-classes)
47 | * [Private Constructors](#private-constructors)
48 | * [Interfaces](#interfaces)
49 | * [Interfaces with Classes](#interfaces-with-classes)
50 | * [Inheritance with Interfaces](#inheritance-with-interfaces)
51 | * [Interfaces as Function Types](#interfaces-as-function-types)
52 | * [Optional Parameters](#optional-parameters)
53 | * [Advanced Types Concepts](#advanced-types-concepts)
54 | * [Intersection Types](#intersection-types)
55 | * [Type Guards](#type-guards)
56 | * [Discriminated Unions](#discriminated-unions)
57 | * [Type Casting](#type-casting)
58 | * [Index Properties](#index-properties)
59 | * [Function Overloads](#function-overloads)
60 | * [Optional Chaining](#optional-chaining)
61 | * [Nullish Coalescing](#nullish-coalescing)
62 | * [Generics](#generics)
63 | * [What are generics?](#what-are-generics)
64 | * [Custom Generic](#custom-generic)
65 | * [Functions](#functions)
66 | * [Constraints](#constraints)
67 | * [KeyOf Constraint](#keyof-constraint)
68 | * [Generic Classes](#generic-classes)
69 | * [Generic Utility Types](#generic-utility-types)
70 | * [Partial](#partial)
71 | * [Readonly](#readonly)
72 | * [Generic vs Union Types?](#generic-vs-union-types)
73 | * [Decorators](#decorators)
74 | * [DISCLAIMER](#disclaimer)
75 | * [Decorators Factories](#decorators-factories)
76 | * [Example](#example)
77 | * [Property Decorators](#property-decorators)
78 | * [Returning in a decorator](#returning-in-a-decorator)
79 | * [Modules](#modules)
80 | * [Namespaces and File Bundling](#namespaces-and-file-bundling)
81 | * [Result](#result)
82 | * [ES6 Modules](#es6-modules)
83 | * [Syntax](#syntax)
84 | * [tsconfig](#tsconfig)
85 | * [IMPORTANT](#important)
86 | * [Webpack](#webpack)
87 | * [What is Webpack?](#what-is-webpack)
88 | * [Install](#install)
89 | * [Configurations](#configurations)
90 | * [How to use Webpack](#how-to-use-webpack)
91 | * [Dev Workflow](#dev-workflow)
92 | * [Production Workflow](#production-workflow)
93 | * [Useful Requirements](#useful-requirements)
94 | * [NodeJS and Express](#nodejs-and-express)
95 | * [Useful stuff](#useful-stuff)
96 | * [Credits](#credits)
97 |
98 |
99 | # Introduction
100 |
101 | ## What is TypeScript?
102 |
103 | TypeScript is a JavaScript superset, which means that it is a language that builds up on JavaScript. It adds **new features and advantages** to JS.
104 |
105 | Browsers and Node.js can't execute TypeScript.
106 |
107 | TypeScript is more of a compiler that compiles code to JavaScript, it basically transforms your code into JS.
108 |
109 | 
110 |
111 | ### What does it add?
112 |
113 | Most importantly, as the name suggests, it adds ``types`` to the JS language. This can be useful to prevent ``runtime`` errors.
114 |
115 | ## Why TypeScript?
116 |
117 | Let's assume this JS code:
118 |
119 | ```javascript
120 | function add(num1, num2) {
121 | return num1 + num2;
122 | }
123 |
124 | add("1", "2");
125 | ```
126 |
127 | We are passing strings to a function that should sum two numbers, this won't generate an error in JS but we will not receive the expected output (unwanted behavior at Runtime).
128 |
129 | To fix this in JS we would need to do something like:
130 |
131 | ```javascript
132 | if (typeof num1 === "number" && typeof num2 === "number")
133 | ```
134 |
135 | TypeScript can really help us in situations like this:
136 |
137 | ```typescript
138 | function add(num1: number, num2: number) {
139 | return num1 + num2;
140 | }
141 | ```
142 |
143 | With this extra syntax we openly state that we should only expect numbers.
144 |
145 |
146 |
147 | ## Requirements
148 |
149 | * Node.js
150 | * TypeScript npm package
151 |
152 | ### Install typescript
153 |
154 | ``npm i -g typescrypt``
155 |
156 |
157 |
158 | ## Overview
159 |
160 | TypeScript adds:
161 |
162 | * Types
163 | * Next-gen JS Features compiled down for older Browsers
164 | * More features such as Interfaces or Generics
165 | * Meta-Programming Features like Decorators
166 | * Rich Configuration Options
167 | * Modern tooling that helps even non TypeScript Projects (especially in IDEs).
168 |
169 |
170 |
171 | # Basics
172 |
173 | ## Core Types
174 |
175 | In TypeScript we have the following types (and more):
176 |
177 | * number (all numbers)
178 | * string
179 | * boolean
180 | * no truthy or falsy values
181 | * object
182 | * array
183 | * any
184 | * it is as flexible as writing normal JS
185 | * undefined
186 | * Function
187 | * unknown
188 | * a value that is not guaranteed to be something.
189 | * void
190 | * never
191 |
192 |
193 |
194 | ### Using types
195 |
196 | ```typescript
197 | function add (num1: number, num2: number) {
198 | return num1 + num2;
199 | }
200 |
201 | const n1 = 10;
202 | const n2 = 3.2;
203 |
204 | const result = add(n1, n2);
205 | ```
206 |
207 | **TypeScript's type system only helps you during development**.
208 |
209 |
210 |
211 | ### Type Inference
212 |
213 | TypeScript is smart enough to recognize some non-explicit variable types such as
214 |
215 | ```typescript
216 | let number1 = 5; // number1: number
217 | ```
218 |
219 | We could do
220 |
221 | ```typescript
222 | let number1: number = 1;
223 | ```
224 |
225 | We can also declare variables without values:
226 |
227 | ```typescript
228 | const n1: number;
229 | ```
230 |
231 |
232 |
233 | ### Object types
234 |
235 | Object properties in TS are stricter than in JS, if we have a `person` object:
236 |
237 | ```typescript
238 | const person = {
239 | name: 'Leonardo',
240 | age: 17
241 | };
242 | ```
243 |
244 | We can't access `person.nickname`.
245 |
246 | This is a 'generic' object, in TS we can be more specific:
247 |
248 | ```typescript
249 | const person: { name: string, age: number } = {
250 | name: 'Leonardo',
251 | age: 17
252 | };
253 | ```
254 |
255 | In this case the object **must only have** a name property and an age property.
256 |
257 | But this is not `best practice` since TS can pick it up on its own just by doing like in the example before this one.
258 |
259 |
260 |
261 | ### Array types
262 |
263 | TS describes an array with ``type[]`` where type can be `string, number ...`
264 |
265 | So we can declare an array like this:
266 |
267 | ```typescript
268 | const arr: string[];
269 | ```
270 |
271 | And we can only add strings.
272 |
273 | If we want a `mixed` array we can use the `any` type.
274 |
275 |
276 |
277 | ## More Types
278 |
279 | TS has some new types.
280 |
281 |
282 |
283 | ### Tuples
284 |
285 | Fixed length and fixed content arrays.
286 |
287 | ```typescript
288 | const person: { name: string, age: number, role: [number, string] } = {
289 | name: 'Leonardo',
290 | age: 17,
291 | role: [1, 'admin']
292 | };
293 | ```
294 |
295 | **NOTE:** ``push`` is allowed in tuples even if, in this example, you can't manually create a 'role' tuple with more than 2 elements (or less than 2).
296 |
297 |
298 |
299 | ### Enum
300 |
301 | They assign labels to numbers and are useful for constant identifiers.
302 |
303 | ```typescript
304 | enum Role { ADMIN, READ_ONLY, AUTHOR };
305 |
306 | const person: {
307 | name: string,
308 | age: number,
309 | role: Role.ADMIN
310 | } = {
311 | name: 'Leonardo',
312 | age: 17,
313 | role: [1, 'admin']
314 | };
315 | ```
316 |
317 | We can assign custom **values** (numbers...) to them if we want.
318 |
319 | ```typescript
320 | enum Role { ADMIN = 100, READ_ONLY = "abc", AUTHOR = 12 };
321 | ```
322 |
323 |
324 |
325 | ## Union Types
326 |
327 | We can specify more than a type to a variable with `union types`.
328 |
329 | ```typescript
330 | const someVariable: string | number;
331 | ```
332 |
333 |
334 |
335 | ## Literal Types
336 |
337 | Literal types are used to specify a precise value to assign to a variable. Especially useful for string identifiers or flags.
338 |
339 | ```typescript
340 | function doSomething(param1: number, param2: number, flag: 'do-this' | 'dont-do-this') {
341 | // code...
342 | }
343 | ```
344 |
345 |
346 |
347 | Here `flag` can only be:
348 |
349 | * do-this
350 | * dont-do-this
351 |
352 |
353 |
354 | ## Type Aliases
355 |
356 | We can create aliases for types:
357 |
358 | ```typescript
359 | type MyCustomType = number | string;
360 | type AnotherType = ':)';
361 | ```
362 |
363 |
364 |
365 | ## Function Return Types
366 |
367 | We can specify a return type to returns:
368 |
369 | ```typescript
370 | function printResult (n1: number, n2: number): void {
371 | console.log('Result: ', + (n1+ n2));
372 | }
373 | ```
374 |
375 | We obviously don't need to specify it in this case.
376 |
377 |
378 |
379 | ### Function as Types
380 |
381 | Variables can hold a ``function``.
382 |
383 | ```typescript
384 | const someVariable: Function;
385 | ```
386 |
387 | We can even be more specific by doing:
388 |
389 | ```typescript
390 | // this is a function with NO arguments that returns a NUMBER
391 | const temp = () => number;
392 | ```
393 |
394 | This can also be useful to specify ``callbacks``.
395 |
396 | ## Unknown Type
397 |
398 | Unknown is more restrictive than `any`.
399 |
400 | For example we CAN'T do:
401 |
402 | ```typescript
403 | let a: unknown;
404 | let b: string;
405 |
406 | a = 5;
407 | a = 'abc';
408 |
409 | // NO
410 | b = a;
411 | ```
412 |
413 | This example would work in case of the `any` type.
414 |
415 | ## Never Type
416 |
417 | ```typescript
418 | function generateError(message: string, code: number): never {
419 | throw { message: message, errorCode: code};
420 | }
421 |
422 | generateCode('Error Message', 500);
423 | ```
424 |
425 | This function `never` returns a value. It is a special case (it crashes our program and stops there).
426 |
427 |
428 |
429 | # TS Compiler
430 |
431 | To compile code we run ``tsc file.ts``. But we can configure it with a `tsconfig.json`.
432 |
433 | ## Command Options
434 |
435 | | --watch | Watch Mode to avoid compiling a file every time |
436 | | ------- | ------------------------------------------------------------ |
437 | | --init | Init a project (compile multiple files and create tsconfig.json) |
438 |
439 | ## Flags in tsconfig
440 |
441 | | exclude | exclude files from compilation / node_modules |
442 | | ------------- | ------------------------------------------------------------ |
443 | | include | include files for compilation |
444 | | files | points/specify files |
445 | | target | ECMAScript target version |
446 | | module | module system |
447 | | lib | specify libraries (DOM) |
448 | | checkJs | reports errors in .js files |
449 | | sourcemap | bridge to connect the js files to the input ts files (we can see ts files in the source tab in the browser) |
450 | | outDir | specify where the created js will be stored |
451 | | rootDir | specify where to grab the ts files |
452 | | noEmitOnError | don't emit js files if an error occurs |
453 |
454 | And many more.
455 |
456 |
457 |
458 | # Classes and Interfaces
459 |
460 | ## Classes
461 |
462 | ```typescript
463 | class Department {
464 | // attributes
465 | name: string;
466 |
467 | // constructor
468 | constructor(n: string) {
469 | this.name = n;
470 | }
471 |
472 | // methods
473 | printName(this: Department) { // ts feature to specify that this method should run only on a Department object
474 | console.log(this.name);
475 | }
476 | }
477 |
478 | const dep = new Department('Dep Name');
479 | ```
480 |
481 |
482 |
483 | ### Private Modifiers
484 |
485 | ```typescript
486 | class Department {
487 | private name: string;
488 | private employees: string[] = [];
489 |
490 | constructor(n: string) {
491 | this.name = n;
492 | }
493 |
494 | printName(this: Department) {
495 | console.log(this.name);
496 | }
497 |
498 | addEmployee(employee: string) {
499 | this.employees.push(employee);
500 | }
501 | }
502 | ```
503 |
504 | Properties are now accessible only in the class (not in [child](#Inheritance) classes).
505 |
506 | In order to share `private` properties to child classes thru [Inheritance](#Inheritance) use the `protected` keyword.
507 |
508 |
509 |
510 | ### Public Modifiers
511 |
512 | Public is the default option so we don't need to set it.
513 |
514 | ```typescript
515 | class Department {
516 | public name: string;
517 | public employees: string[] = [];
518 |
519 | constructor(n: string) {
520 | this.name = n;
521 | }
522 |
523 | printName(this: Department) {
524 | console.log(this.name);
525 | }
526 |
527 | addEmployee(employee: string) {
528 | this.employees.push(employee);
529 | }
530 | }
531 | ```
532 |
533 |
534 |
535 | ### Shorthand Initialization
536 |
537 | ```typescript
538 | class Department {
539 | // public name: string;
540 | // public employees: string[] = [];
541 |
542 | constructor(private name: string, public employees: string[]) {
543 | // this.name = n;
544 | }
545 |
546 | printName(this: Department) {
547 | console.log(this.name);
548 | }
549 |
550 | addEmployee(employee: string) {
551 | this.employees.push(employee);
552 | }
553 | }
554 | ```
555 |
556 |
557 |
558 | ### Read-only Modifier
559 |
560 | ```typescript
561 | class Department {
562 | private readonly name: string;
563 | private employees: string[] = [];
564 |
565 | constructor(n: string) {
566 | this.name = n;
567 | }
568 |
569 | printName(this: Department) {
570 | console.log(this.name);
571 | }
572 |
573 | addEmployee(employee: string) {
574 | this.employees.push(employee);
575 | }
576 | }
577 | ```
578 |
579 | We can't modify the name property.
580 |
581 |
582 |
583 | ### Inheritance
584 |
585 | ```typescript
586 | class Department {
587 | private readonly name: string;
588 | private employees: string[] = [];
589 |
590 | constructor(n: string) {
591 | this.name = n;
592 | }
593 |
594 | printName(this: Department) {
595 | console.log(this.name);
596 | }
597 |
598 | addEmployee(employee: string) {
599 | this.employees.push(employee);
600 | }
601 | }
602 |
603 | class ITDepartment extends Department {
604 | constructor(id: string) {
605 | super('IT');
606 | this.id = id;
607 | }
608 | }
609 | ```
610 |
611 | We can also override parent methods in child classes by re-declaring them.
612 |
613 |
614 |
615 | ### Getters and Setters
616 |
617 | In order to access `private` attributes from the outside of a class we can implement `getters` and `setters`.
618 |
619 | ```typescript
620 | class Department {
621 | private name: string;
622 |
623 | constructor(n: string) {
624 | this.name = n;
625 | }
626 |
627 | get name(this: Department) {
628 | return this.name;
629 | }
630 |
631 | set name(value: string) {
632 | this.name = value;
633 | }
634 |
635 | }
636 |
637 | const d = new Department();
638 |
639 | // Getter
640 | console.log(d.name) // with no ()
641 |
642 | // Setter
643 | d.name = "Name";
644 | ```
645 |
646 | We access them as `properties` and not methods.
647 |
648 |
649 |
650 | ### Static Properties and Methods
651 |
652 | Properties and Methods that we don't access from an instance. An example is `Math`.
653 |
654 | ```typescript
655 | class Department {
656 | private name: string;
657 |
658 | constructor(n: string) {
659 | this.name = n;
660 | }
661 |
662 | static createEmployee(name: string) {
663 | return { name: name };
664 | }
665 |
666 | }
667 |
668 | Department.createEmployee('Leo');
669 | ```
670 |
671 |
672 |
673 | ### Abstract Classes
674 |
675 | Back to Inheritance, we can force developers to implement a certain method in a class (especially child ones).
676 |
677 | ```typescript
678 | abstract class Department {
679 | private readonly name: string;
680 | private employees: string[] = [];
681 |
682 | constructor(n: string) {
683 | this.name = n;
684 | }
685 |
686 | printName(this: Department) {
687 | console.log(this.name);
688 | }
689 |
690 | addEmployee(employee: string) {
691 | this.employees.push(employee);
692 | }
693 |
694 | // we are just saying that every child must implement this method.
695 | abstract mandatoryMethod(this: Department): void;
696 | }
697 |
698 | class ITDepartment extends Department {
699 | constructor(id: string) {
700 | super('IT');
701 | this.id = id;
702 | }
703 |
704 | mandatoryMethod() {
705 | // code
706 | }
707 | }
708 | ```
709 |
710 | `abstract` classes **CAN'T BE INSTANTIATED**
711 |
712 |
713 |
714 | ### Private Constructors
715 |
716 | Singleton Pattern = have only one instance of a class.
717 |
718 | With private constructors we can't use `new ClassName()` outside of the class.
719 |
720 | ```typescript
721 | class ITDepartment extends Department {
722 | // we need to provide an instance otherwise we can't create it with the 'new' keyword
723 | private static instance: ITDepartment;
724 |
725 | private constructor(id: string) {
726 | super('IT');
727 | this.id = id;
728 | }
729 |
730 | static getInstance() {
731 | if (ITDepartment.instance) {
732 | return this.instance;
733 | }
734 |
735 | this.instance = new ITDepartment();
736 | }
737 | }
738 |
739 | const it = ITDepartment.getInstance();
740 | const it2 = ITDepartment.getInstance();
741 |
742 | // it === it2
743 | ```
744 |
745 |
746 |
747 | ## Interfaces
748 |
749 | An interfaces describes the structure of an object (not the same thing as a custom type).
750 |
751 | ```typescript
752 | interface Person {
753 | name: string;
754 | age: number;
755 |
756 | printName(): void;
757 | }
758 |
759 | let user: Person;
760 |
761 | user = {
762 | name: 'Leonardo',
763 | age: 17,
764 | printName: function () {
765 | console.log(this.name);
766 | }
767 | }
768 | ```
769 |
770 | We can also add `readonly` to properties.
771 |
772 | ### Interfaces with Classes
773 |
774 | We can implement an interface in a class.
775 |
776 | ```typescript
777 | interface Greetable {
778 | name: string;
779 |
780 | greet(): void;
781 | }
782 |
783 | class Person implements Greetable {
784 | name: string;
785 |
786 | constructor(name: string) {
787 | this.name = name;
788 | }
789 |
790 | greet() {
791 | console.log(this.name);
792 | }
793 | }
794 |
795 | let user: Greetable;
796 |
797 | user = new Person('Leo');
798 | ```
799 |
800 | The interface acts like a strict blueprint.
801 |
802 |
803 |
804 | ### Inheritance with Interfaces
805 |
806 | ```typescript
807 | interface Named {
808 | readonly name: string
809 | }
810 |
811 | interface Greetable extends Named {
812 | greet(): void
813 | }
814 |
815 | interface AnotherInterface {
816 | readonly someValue: string
817 | }
818 |
819 | class Person implements Greetable, AnotherInterface {
820 | name: string;
821 | someValue: string;
822 |
823 | constructor(name: string) {
824 | this.name = name;
825 | }
826 |
827 | greet() {
828 | console.log(this.name);
829 | }
830 | }
831 | ```
832 |
833 |
834 |
835 | ### Interfaces as Function Types
836 |
837 | ```typescript
838 | interface AddFn {
839 | (a: number, b:number): number;
840 | }
841 |
842 | let add: AddFn;
843 |
844 | add = (n1: number, n2: number) => {
845 | return n1 + n2;
846 | }
847 | ```
848 |
849 |
850 |
851 | ### Optional Parameters
852 |
853 | ```typescript
854 | interface Person {
855 | name: string;
856 | lastName?: string
857 | }
858 | ```
859 |
860 | `lastName` will be optional
861 |
862 | **We can have optional parameters even in function and classes.**
863 |
864 |
865 |
866 | # Advanced Types Concepts
867 |
868 |
869 |
870 | ## Intersection Types
871 |
872 | ```typescript
873 | type Admin = {
874 | name: string;
875 | priviliges: string[];
876 | };
877 |
878 | type Employee = {
879 | name: string;
880 | startDate: Date;
881 | };
882 |
883 | type ElevatedEmployee = Admin & Employee;
884 | ```
885 |
886 | It's the same thing as `extending` interfaces.
887 |
888 | ```typescript
889 | type Type1 = string | number;
890 | type Type2 = number | boolean;
891 |
892 | type Intersection = Type1 & Type2; // --> number
893 | ```
894 |
895 |
896 |
897 | ## Type Guards
898 |
899 | ```typescript
900 | type Combinable = string | number;
901 |
902 | function add(a: Combinable, b: Combinable) {
903 | if (typeof a === 'string' || typeof b === 'string') { // this is a type guard
904 | return a.toString() + b.toString();
905 | }
906 |
907 | return a + b;
908 | }
909 | ```
910 |
911 | In case of combined types:
912 |
913 | ```typescript
914 | type Admin = {
915 | name: string;
916 | priviliges: string[];
917 | };
918 |
919 | type Employee = {
920 | name: string;
921 | startDate: Date;
922 | };
923 |
924 | type UnknownEmp = Admin | Employee; // admin OR employee
925 |
926 | function printEmpInfo(emp: UnknownEmp) {
927 | console.log(emp.name);
928 | if ('priviliges' in emp) { // type guard
929 | console.log(emp.priviliges);
930 | }
931 | }
932 | ```
933 |
934 | Or we could use `instanceof` as a type guard.
935 |
936 |
937 |
938 | ## Discriminated Unions
939 |
940 | ```typescript
941 | interface Bird {
942 | type: 'bird'; // literal type
943 | flyingSpeed: number;
944 | }
945 |
946 | interface Horse {
947 | type: 'horse';
948 | runningSpeed: number;
949 | }
950 |
951 | type Animal = Bird | Horse;
952 |
953 | function moveAnimal(animal: Animal) {
954 | let speed;
955 | switch (animal.type) {
956 | case 'bird':
957 | speed = animal.flyingSpeed;
958 | case 'horse':
959 | speed = animal.runningSpeed;
960 | }
961 |
962 | console.log("Moving at speed: ", speed);
963 | }
964 | ```
965 |
966 |
967 |
968 | ## Type Casting
969 |
970 | We basically specify the type of something, useful especially when manipulating the DOM:
971 |
972 | ```typescript
973 | const inputEl = document.getElementById('user-input')!; // ! means that we tell TS not to worry about the fact that maybe inputEl doesn't exist.
974 |
975 | inputEl.value = "Hi there!"; // we can safely access value because we are telling TS that inputEl is going to be HTMLInputElement, otherwise it complains about the fact that the value property may not exist.
976 | ```
977 |
978 | But this uses the same syntax as some React stuff (I don't know React I use Vue xD) so in that case we use the `as` keyword:
979 |
980 | ```typescript
981 | const inputEl = document.getElementById('user-input')! as HTMLInputElement;
982 | ```
983 |
984 |
985 |
986 | ## Index Properties
987 |
988 | We use them to build flexible interfaces.
989 |
990 | ```typescript
991 | interface ErrorCOntainer { // flexible container
992 | [prop: string]: string; // every property we add must be a string, it is like a 'general keyword'
993 | id: string; // OK
994 | code: number; // NO
995 | }
996 | ```
997 |
998 | We specify that properties names and their values must be strings BUT we don't actually know the properties.
999 |
1000 |
1001 |
1002 | ## Function Overloads
1003 |
1004 | ```typescript
1005 | type Combinable = string | number;
1006 |
1007 | function add(a: number, b: number): number; // if both params are number then return a number
1008 | function add(a: Combinable, b: Combinable) {
1009 | if (typeof a === 'string' || typeof b === 'string') { // this is a type guard
1010 | return a.toString() + b.toString();
1011 | }
1012 |
1013 | return a + b;
1014 | }
1015 | ```
1016 |
1017 | It is similar to an if statement but related to function params. It gets 'translated' to: if we call the function 'add' with two numbers then expect back a number. So later in the code we can handle the result as a number (we can use numbers related methods without warnings).
1018 |
1019 |
1020 |
1021 | ## Optional Chaining
1022 |
1023 | Useful when we fetch data from an API and we don't know if the result as some properties.
1024 |
1025 | ```typescript
1026 | const fetchedData = {
1027 | id: 'u1',
1028 | name: 'Leo',
1029 | // job: {title: 'CEO', payCheck: 10000} ---> imagine that this doesn't get fetched correctly
1030 | };
1031 |
1032 | console.log(fetchedData.job.title) // ERROR
1033 | ```
1034 |
1035 | We can solve this by adding a `?` to the object
1036 |
1037 | ```typescript
1038 | console.log(fetchedData.job?.title) // ERROR
1039 | ```
1040 |
1041 |
1042 |
1043 | ## Nullish Coalescing
1044 |
1045 | If we don't know if a piece of data is `null` we can do:
1046 |
1047 | ```typescript
1048 | const gotData = null; // data that we don't know if it is NULL
1049 |
1050 | const storedData = gotData || 'Default'; // JS
1051 | const storedData = gotData ?? 'Default'; // TS --> ONLY IF gotData is null or undefined
1052 | ```
1053 |
1054 |
1055 |
1056 | # Generics
1057 |
1058 | They don't exist in JavaScript but the concept of Generic Types exist in other programming languages.
1059 |
1060 | An array is actually a `generic`.
1061 |
1062 | ## What are generics?
1063 |
1064 | ``Array``
1065 |
1066 | A generic type is a type which is kinda connected with some other types and is really flexible in terms of what type the other type is.
1067 |
1068 | The `Array` type doesn't really care about what data you store in it.
1069 |
1070 | ```typescript
1071 | const names: Array = []; // same thing as string[]
1072 | ```
1073 |
1074 |
1075 |
1076 | Another generic type is `Promise`.
1077 |
1078 | ```typescript
1079 | const promise = new Promise((resolve, reject) => {
1080 | setTimeout(() => {
1081 | resolve('Done');
1082 | }, 1000)
1083 | });
1084 | ```
1085 |
1086 | Here the type of the promise is `Promise` because it will eventually resolve into something.
1087 |
1088 | So we can say:
1089 |
1090 | ```typescript
1091 | const promise: Promise = new Promise((resolve, reject) => {
1092 | setTimeout(() => {
1093 | resolve('Done');
1094 | }, 1000)
1095 | });
1096 | ```
1097 |
1098 | We specify that the promise eventually resolves into a `string`.
1099 |
1100 | Generic types gives us more safety.
1101 |
1102 |
1103 |
1104 | ## Custom Generic
1105 |
1106 | ### Functions
1107 |
1108 | ```typescript
1109 | function merge(objA: object, objB: object) {
1110 | return Object.assign(objA, objB);
1111 | }
1112 |
1113 | const merged = merge({name: 'Leo'}, {age: 17});
1114 | merged.age; // TS ERROR, typescript doesn't know the properties (we could do TYPE CASTING or GENERIC TYPES)
1115 | ```
1116 |
1117 | ```typescript
1118 | function merge(objA: T, objB: U) { //T and U are cusotm identifiers
1119 | return Object.assign(objA, objB);
1120 | }
1121 |
1122 | // THIS FUNCTION RETURNS THE INTERSECTION OF T AND U
1123 | const merged = merge({name: 'Leo'}, {age: 17}); // name: string & age: number
1124 | ```
1125 |
1126 | They are used to specify that what we store in the result is the intersection between the parameters (we actually don't know what they are 'made of').
1127 |
1128 | We can also specify what parameters we are passing:
1129 |
1130 | ```typescript
1131 | const merged = merge<{name: string}, {age: number}>({name: 'Leo'}, {age: 17});
1132 | ```
1133 |
1134 | **But this is redundant.**
1135 |
1136 |
1137 |
1138 | ### Constraints
1139 |
1140 | Sometimes we want to **restrict** the types of generic types.
1141 |
1142 | ```typescript
1143 | function merge(objA: T, objB: U) { //T and U are cusotm identifiers
1144 | return Object.assign(objA, objB);
1145 | }
1146 | ```
1147 |
1148 | So we have improved the merge function by reducing unwanted behaviors such as passing just a number to the `merge` function.
1149 |
1150 | We can also improve this by creating custom `interfaces` and using them with `extends ...` if we only care about certain params properties.
1151 |
1152 |
1153 |
1154 | #### KeyOf Constraint
1155 |
1156 | ```typescript
1157 | function extractAndConvert(obj: object, key: string) {
1158 | return obj[key]; // TS ERROR since we don't know if the object has the key property
1159 | }
1160 | ```
1161 |
1162 | We can fix this by using `generics`
1163 |
1164 | ```typescript
1165 | function extractAndConvert(obj: T, key: U) {
1166 | return obj[key]; // OK
1167 | }
1168 | ```
1169 |
1170 |
1171 |
1172 | ## Generic Classes
1173 |
1174 | ```typescript
1175 | class DataStorage {
1176 | private data: T[] = [];
1177 |
1178 | addItem(item: T) {
1179 | this.data.push(item);
1180 | }
1181 |
1182 | removeItem(item: T) {
1183 | if (this.data.indexOf(item) !== -1) this.data.splice(this.data.indexOf(item), 1);
1184 | }
1185 |
1186 | getItems() {
1187 | return [...this.data];
1188 | }
1189 | }
1190 |
1191 | const textStorage = new DataStorage();
1192 | const numberStorage = new DataStorage();
1193 | ```
1194 |
1195 |
1196 |
1197 | ## Generic Utility Types
1198 |
1199 | Special Utility Types.
1200 |
1201 | #### Partial
1202 |
1203 | ```typescript
1204 | interface Article {
1205 | title: string;
1206 | description: string;
1207 | }
1208 |
1209 | function createArticle(title: string, description: string): Article {
1210 | const toServe: Partial = {}; // This is an object that in the **end** will be an Article
1211 |
1212 | // building manually, maybe we have to do some filtering
1213 | toServe.title = title;
1214 | toServe.decription = description;
1215 |
1216 | return toServe as Article;
1217 | }
1218 | ```
1219 |
1220 |
1221 |
1222 | ### Readonly
1223 |
1224 | ```typescript
1225 | const names: Readonly = ['Leo', 'Anna'];
1226 | ```
1227 |
1228 |
1229 |
1230 | ## Generic vs Union Types?
1231 |
1232 | ```typescript
1233 | class DataStorage {
1234 | private data: T[] = [];
1235 |
1236 | addItem(item: T) {
1237 | this.data.push(item);
1238 | }
1239 |
1240 | removeItem(item: T) {
1241 | if (this.data.indexOf(item) !== -1) this.data.splice(this.data.indexOf(item), 1);
1242 | }
1243 |
1244 | getItems() {
1245 | return [...this.data];
1246 | }
1247 | }
1248 | ```
1249 |
1250 | ```typescript
1251 | class DataStorage {
1252 | private data: (string | number | boolean)[] = [];
1253 |
1254 | addItem(item: string | number | boolean) {
1255 | this.data.push(item);
1256 | }
1257 |
1258 | removeItem(item: string | number | boolean) {
1259 | if (this.data.indexOf(item) !== -1) this.data.splice(this.data.indexOf(item), 1);
1260 | }
1261 |
1262 | getItems() {
1263 | return [...this.data];
1264 | }
1265 | }
1266 | ```
1267 |
1268 | In the second example we allow **mixed** types, to fix that we would have to do ``string[] | number[] | boolean[]`` BUT then we would have a problem while adding and removing stuff because with union types we allow some types every time we call that specific action.
1269 |
1270 | So generic types makes us choose a type of data every time from a range of types, it doesn't accept mixed stuff.
1271 |
1272 | In order words generics locks in a specific type.
1273 |
1274 |
1275 |
1276 | # Decorators
1277 |
1278 | ## DISCLAIMER
1279 |
1280 | Decorators are much more in depth than how they are described in this chapter so more knowledge [here](More [here](https://www.typescriptlang.org/docs/handbook/decorators.html)).
1281 |
1282 |
1283 |
1284 | Writing code that's easier to use for developers.
1285 |
1286 | Decorators are usually about classes and they are **functions**.
1287 |
1288 | **MUST USE WITH ES6 AND EXPERIMENTAL-DECORATORS in TSCONFIG.JSON**
1289 |
1290 |
1291 |
1292 | ```typescript
1293 | function Logger(constructor: Function) { // decorators expect arguments
1294 | console.log("Logging");
1295 | console.log(constructor);
1296 | }
1297 |
1298 | @Logger
1299 | class Person {
1300 | name: 'Leo';
1301 |
1302 | constructor() {
1303 | console.log("Creating person...");
1304 | }
1305 | }
1306 | ```
1307 |
1308 | They run without an instance, so they execute when you define a class.
1309 |
1310 |
1311 |
1312 | ## Decorators Factories
1313 |
1314 | They create decorators that we can configure.
1315 |
1316 | ```typescript
1317 | function Logger(logString: string) {
1318 | return function(constructor: Function) {
1319 | console.log(logString);
1320 | console.log(constructor);
1321 | }
1322 | }
1323 |
1324 | @Logger('LOGGING-PERSON')
1325 | class Person {
1326 | name: 'Leo';
1327 |
1328 | constructor() {
1329 | console.log("Creating person...");
1330 | }
1331 | }
1332 | ```
1333 |
1334 |
1335 |
1336 | ## Example
1337 |
1338 | ```typescript
1339 | function WithTemplate(template: string, hookId: string) {
1340 | return function(_: Function) { // we could pass a class constructor
1341 | const hookEl = document.getElementById(hookId);
1342 | if (hookEl) {
1343 | hookEl.innerHTML = template;
1344 | }
1345 | }
1346 | }
1347 |
1348 | @WithTemplate('Lorem Ipsum
', 'app'); // app is an ID on a DIV
1349 | ```
1350 |
1351 | This is like an Angular Decorator.
1352 |
1353 | We can also have multiple decorators per class and they execute from the bottom to the top.
1354 |
1355 |
1356 |
1357 | ## Property Decorators
1358 |
1359 | ```typescript
1360 | function Log(target: any, propertyName: string | Symbol) {
1361 | console.log(target, propertyName);
1362 | }
1363 |
1364 | class Product {
1365 | @Log
1366 | title: string;
1367 |
1368 | constructor(t: string) {
1369 | this.title = t;
1370 | }
1371 |
1372 | }
1373 | ```
1374 |
1375 | **We can also create decorators for methods and accessors**.
1376 |
1377 | ## Returning in a decorator
1378 |
1379 | When can also return stuff in decorators, especially we can return classes.
1380 |
1381 |
1382 |
1383 | NOTE: this will decorate a class with a ``name`` property
1384 |
1385 | ```typescript
1386 | function WithTemplate(template: string, hookId: string) {
1387 | return function(original_constructor: T) {
1388 | return class extends original_constructor { // based on the og constructor
1389 | // adding new functionalities to the decorated class, this runs when the class is instantiated
1390 | constructor(..._: any[]) {
1391 | super();
1392 | const hookEl = document.getElementById(hookId);
1393 | if (hookEl) {
1394 | hookEl.innerHTML = this.name;
1395 | }
1396 | }
1397 | }
1398 | }
1399 | }
1400 | ```
1401 |
1402 | This basically replace the class that it decorated by keeping its original values.
1403 |
1404 | We can also return in method decorators.
1405 |
1406 |
1407 |
1408 | # Modules
1409 |
1410 | ## Namespaces and File Bundling
1411 |
1412 | Use "namespace" code syntax to group code. Bundled compilation is possible.
1413 |
1414 | In a specific file (``fileName.ts``)
1415 |
1416 | ```typescript
1417 | namespace AModule {
1418 | // code here
1419 | export interface numberOne {}
1420 | export interface numberTwo {}
1421 | }
1422 | ```
1423 |
1424 | And we import it as follows:
1425 |
1426 | ```typescript
1427 | ///
1428 | ```
1429 |
1430 | **But we can't access the single interfaces without putting the "main code" into a namespace with the same name as the exported one.**
1431 |
1432 |
1433 |
1434 | ### Result
1435 |
1436 | We get back multiple files but **they aren't linked between each other**. In order to link them we have to set the ``outFile`` option in `tsconfig` which bundles everything in one file and we also need to set the `module` option to `amd`.
1437 |
1438 | ## ES6 Modules
1439 |
1440 | Modern JavaScript uses ES6 Imports/Exports. TypeScript also supports this. Bundling via third-party tools is possible.
1441 |
1442 | ### Syntax
1443 |
1444 | ```typescript
1445 | import defModule from './path/file.js'
1446 | import { module } from './path/another.js'
1447 | ```
1448 |
1449 | As long as we don't use a ``webpack`` make sure to add `.js` even if we are developing in TypeScript or just omit the extension (this can cause some errors depending on you environment).
1450 |
1451 |
1452 |
1453 | We can obviously use ``default`` exports and other features.
1454 |
1455 | ### tsconfig
1456 |
1457 | We need to set these flags in the ``tsconfig.json``
1458 |
1459 | ```
1460 | target: es6 (or higher)
1461 | module: es2015 (or higher)
1462 | ```
1463 |
1464 | We can't use the `outfile` flag.
1465 |
1466 | ### IMPORTANT
1467 |
1468 | In a HTML file we have to add the `module` flag.
1469 |
1470 | ```html
1471 |
1472 | ```
1473 |
1474 | Modules get executed the first time they get imported in the app even though we import a single one multiple times in different files.
1475 |
1476 |
1477 |
1478 | # Webpack
1479 |
1480 | ## What is Webpack?
1481 |
1482 | Splitting code in multiple modules result in a lot of ``http requests``.
1483 |
1484 | Webpack is a tool to bundle files together (Build Orchestration Tool).
1485 |
1486 | It also optimizes our code (minimized code, less imports, etc).
1487 |
1488 | ## Install
1489 |
1490 | ```
1491 | npm install --save-dev webpack webpack-cli webpack-dev-server typescript ts-loader
1492 | ```
1493 |
1494 | **Installing a copy of `typescript` in every project is a good practice in order not to break an existing project if typescript gets updated with deep new features.**
1495 |
1496 | ## Configurations
1497 |
1498 | In `tsconfig.json`
1499 |
1500 | ```
1501 | target: es5/es6/esnext
1502 | module: es2015/es6
1503 | NO rootDir
1504 | sourceMap: true --> helps debugging
1505 | ```
1506 |
1507 | ``webpack.config.js``
1508 |
1509 | ```javascript
1510 | const path = require('path');
1511 |
1512 | module.exports = {
1513 | entry: './src/apps.ts', // root project entry
1514 | output: {
1515 | filename: 'bundle.js' // out file
1516 | path: path.resolve(__dirname, 'dist') // absolute path (same as the on ein tsconfig)
1517 | },
1518 | devtool: 'inline-source-map', // alreay generated source maps (tsconfig)
1519 | // instructions on how to deal ts files
1520 | module: {
1521 | rules: [
1522 | {
1523 | test: /\.ts$/,
1524 | use: 'ts-loader',
1525 | exclude: /node_modules/
1526 | }
1527 | ]
1528 | },
1529 |
1530 | resolve: {
1531 | extensions: ['.ts', '.js']
1532 | }
1533 | };
1534 | ```
1535 |
1536 | REMOVE ALL EXTENSIONS IN IMPORT STATEMENTS
1537 |
1538 | ## How to use Webpack
1539 |
1540 | in ``package.json``
1541 |
1542 | ```json
1543 | "build" : "webpack"
1544 | ```
1545 |
1546 | And then we can use the generated file.
1547 |
1548 | ## Dev Workflow
1549 |
1550 | in `package.json`
1551 |
1552 | ```json
1553 | "dev" : "webpack-dev-server"
1554 | ```
1555 |
1556 | **In dev mode the bundle is generated in memory only**
1557 |
1558 | So we need to add (in `webpack.config.js`):
1559 |
1560 | ```js
1561 | mode: 'development' // fewer optimizations
1562 | publicPath: 'dist'
1563 | ```
1564 |
1565 | ## Production Workflow
1566 |
1567 | ### Useful Requirements
1568 |
1569 | ```
1570 | npm i --save-dev clean-webpack-plugin
1571 | ```
1572 |
1573 | To clear the bundle every time we have a new version.
1574 |
1575 | ``webpack.config.prod.js``
1576 |
1577 | ```javascript
1578 | const path = require('path');
1579 | const CleanPlugin = require('clean-webpack-plugin');
1580 |
1581 | module.exports = {
1582 | mode: 'production',
1583 | entry: './src/apps.ts', // root project entry
1584 | output: {
1585 | filename: 'bundle.js' // out file
1586 | path: path.resolve(__dirname, 'dist') // absolute path (same as the on ein tsconfig)
1587 | },
1588 | devtool: 'none', // alreay generated source maps (tsconfig)
1589 | // instructions on how to deal ts files
1590 | module: {
1591 | rules: [
1592 | {
1593 | test: /\.ts$/,
1594 | use: 'ts-loader',
1595 | exclude: /node_modules/
1596 | }
1597 | ]
1598 | },
1599 |
1600 | resolve: {
1601 | extensions: ['.ts', '.js']
1602 | },
1603 | plugins: [
1604 | new CleanPlugin.CleanWebpackPlugin()
1605 | ]
1606 | };
1607 | ```
1608 |
1609 |
1610 |
1611 | ``package.json``
1612 |
1613 | ```json
1614 | "build" : "webpack --config webpack.config.prod.js"
1615 | ```
1616 |
1617 |
1618 |
1619 | # NodeJS and Express
1620 |
1621 | Node is not able to run `TypeScript` files. We could install ``ts-node`` but it is not good for production.
1622 |
1623 | What we can do is:
1624 |
1625 | * npm init
1626 | * tsc --init
1627 | * `target: esnext`
1628 | * `moduleResolution: node`
1629 | * npm i express
1630 | * npm i --save-dev nodemon
1631 | * npm i --save-dev @types/node
1632 | * npm i --save-dev @types/express
1633 |
1634 | ## Useful stuff
1635 |
1636 | ```typescript
1637 | import { RequestHandler } from 'express';
1638 |
1639 | export const controller: RequestHandler = (req, res, next) => {}
1640 | ```
1641 |
1642 |
1643 |
1644 | ```typescript
1645 | import { Request, Response, NextFunction } from 'express';
1646 | ```
1647 |
1648 |
1649 |
1650 |
1651 |
1652 | # Credits
1653 |
1654 | Extended cheatsheet made by Leonardo Folgon and made by following various resources on the internet.
1655 |
--------------------------------------------------------------------------------
/cheatsheet/ts.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f0lg0/ts-extended-cheatsheet/e66c1aaea0558b77d4246f81348aa2e2d94cd63b/cheatsheet/ts.pdf
--------------------------------------------------------------------------------