├── LICENSE
├── README.md
├── cheat-sheet.fig
├── cheat-sheet.md
├── cheat-sheet.pdf
├── cheat-sheet.png
├── images
├── error-message.png
├── intersections-venn.png
├── sets-venn.png
└── union-venn.png
└── small.png
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Carl Riis
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 |
Magic TypeScript
2 | A cheat-sheet of TypeScript’s most important/magic features
3 |
4 | Read the blog post about it here
5 |
6 | [
](https://carltheperson.com/images/magic-typescript/magic-typescript.png)
7 |
8 | # The cheat sheet
9 |
10 | [Click here to view it in full size](https://carltheperson.com/images/magic-typescript/magic-typescript.png)
11 |
12 | # Document version
13 |
14 | If you prefer to read the cheat sheet in a simple document, you can find that [here](cheat-sheet.md).
15 |
16 | There is a PDF version [here](cheat-sheet.pdf).
17 |
18 | # Source
19 |
20 | [This is the Figma file.](cheat-sheet.fig)
21 |
22 | # Issues
23 |
24 | I may have made a mistake or two. If you find one, feel free to open an issue, and I will fix it.
25 |
26 | # License
27 |
28 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
29 |
--------------------------------------------------------------------------------
/cheat-sheet.fig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/cheat-sheet.fig
--------------------------------------------------------------------------------
/cheat-sheet.md:
--------------------------------------------------------------------------------
1 | # Magic TypeScript
2 |
3 | **A cheat sheet of TypeScript’s most important/magic features**
4 |
5 | # Subsets
6 |
7 | This is the most important concept to understand about TypeScript. Think of all types as sets of values.
8 |
9 | ```ts
10 | never; // Special type meaning empty set
11 | ("dog"); // Unit type. A set with only the value "dog"
12 | "dog" | "cat"; // Union type. A set with only the values "dog" and "cat"
13 | string; // A set containing every string value
14 | number; // A set containing every number
15 | any; // Special type which contains every value in its set
16 | ```
17 |
18 | You can use one type in place of another as long as its set overlaps fully. In other words:
19 |
20 | 
21 |
22 | Examples:
23 |
24 | ```ts
25 | type Type1 = "apple" | "orange";
26 | type Type2 = "apple";
27 | type Type3 = "apple" | "banana";
28 |
29 | const func = (food: Type1) => {
30 | /* ... */
31 | };
32 | // This function will let you pass in type Type1 and Type2 but not Type3
33 | ```
34 |
35 | ```ts
36 | interface Pet {
37 | name: string;
38 | }
39 |
40 | interface Dog {
41 | name: string;
42 | favouriteToy: string;
43 | furType: "curly" | "flat";
44 | }
45 | // Any function needing Pet as a parameter will happily also accept Dog
46 | ```
47 |
48 | ```ts
49 | const fetchTranslation = (language: "English" | "Spanish" | "German") => {
50 | /* ... */
51 | };
52 | const language = getRandomLanguage(); // Type is string
53 | fetchTranslation(language);
54 | // This does NOT work since 'string' could be a bunch of values that
55 | // are not "English", "Spanish", or "German"
56 | ```
57 |
58 | ### Error message translation
59 |
60 | 
61 |
62 | This means that the set of TypeA is not contained inside the set of TypeB.
63 |
64 | # Inference
65 |
66 | If you don't specify a type TypeScript will make a guess and assign one.
67 |
68 | ```ts
69 | let name = "Carl"; // Type is string
70 | ```
71 |
72 | Using _const_ makes TypeScript infer a different type. This is because _const_ makes the variable immutable.
73 |
74 | ```ts
75 | const name = "Carl"; // Type is "Carl"
76 | ```
77 |
78 | ### as const
79 |
80 | TypeScript normally infers a pretty wide type for objects, but you can force it to narrow it with _as const._
81 |
82 | ```ts
83 | const person = { name: "Carl" }; // Type is { name: string }
84 | const person = { name: "Carl" } as const; // Type is { name: "Carl" }
85 | ```
86 |
87 | ### Narrowing with conditions
88 |
89 | TypeScript can narrow a type depending on the context.
90 |
91 | ```ts
92 | const user = getUserFromId("123"); // Type is User | null
93 | if (user) {
94 | // Type of user is User
95 | } else {
96 | // Type of user is null
97 | }
98 | ```
99 |
100 | # Type guards
101 |
102 | You can help the type system infer types with type guards. They allow you to define your own checks for types.
103 |
104 | ```ts
105 | const isCat = (pet: Cat | Dog): pet is Cat => {
106 | return pet.latestMessage.includes("meow");
107 | };
108 |
109 | const pet = getRandomPet(); // Type is Cat | Dog
110 | if (isCat(pet)) {
111 | // Type is Cat
112 | } else {
113 | // Type is Dog
114 | }
115 | ```
116 |
117 | # Unions
118 |
119 | A union type is a type that has the possibility to be other types. When working with unions of objects, you can only access properties that both types have in common.
120 |
121 | 
122 |
123 | ```ts
124 | interface Cat {
125 | isSleeping: boolean;
126 | isMeowing: boolean;
127 | }
128 |
129 | interface Dog {
130 | isSleeping: boolean;
131 | isPlayingFetch: boolean;
132 | }
133 |
134 | // Same as { isSleeping: boolean }
135 | type Pet = Cat | Dog;
136 |
137 | const dog: Pet = getRandomDog(); // OK
138 | ```
139 |
140 | # Intersections
141 |
142 | Intersections are related to union types, but they combine everything into one set.
143 |
144 | 
145 |
146 | ```ts
147 | interface Person {
148 | name: string;
149 | age: number;
150 | }
151 |
152 | interface LocationData {
153 | country: string;
154 | address: string;
155 | }
156 |
157 | type PersonWithLocation = Person & LocationData; // Same as:
158 | // { name: string, age: number, country: string, address: string }
159 |
160 | // TS Error: Type Person is missing: country, address
161 | const person: PersonWithLocation = getRandomPerson();
162 | ```
163 |
164 | # Enums
165 |
166 | Enums let you define a set of named constants. They act very similar to union types.
167 |
168 | ```ts
169 | enum SeverityCode {
170 | CRITICAL = 0
171 | WARNING = 200
172 | NORMAL = 1000
173 | }
174 |
175 | if (getSeverity() === SeverityCode.CRITICAL) {
176 | // Panic!
177 | }
178 | ```
179 |
180 | This is a very broad type that will let you use keys you haven't defined. You can define the allowed set for the keys with the _in_ keyword. It will even warn you if you don't define a value for all the keys. The global utility type _Record_ uses this.
181 |
182 | ```ts
183 | const ages: { [name in "Ernest" | "Chester"]: number } = {
184 | Ernest: 36,
185 | Chester: 21,
186 | };
187 | ages["Ernest"]; // 36
188 | ages["Harvey"]; // TS Error: Property 'Harvey' does not exist ...
189 |
190 | // Exact same thing as above
191 | const ages: Record<"Ernest" | "Chester", number> = {
192 | Ernest: 36,
193 | Chester: 21,
194 | };
195 | ```
196 |
197 | # Index signatures (key/value)
198 |
199 | Index signatures allow you to define types for key value pairs.
200 |
201 | ```ts
202 | const ages: { [name: string]: number } = {
203 | Ernest: 36,
204 | Chester: 21,
205 | };
206 | ages["Ernest"]; // 36
207 | ```
208 |
209 | This is a very broad type that will let you use keys you haven't defined. You can define the allowed set for the keys with the _in_ keyword. It will even warn you if you don't define a value for all the keys. The global utility type _Record_ uses this.
210 |
211 | ```ts
212 | const ages: { [name in "Ernest" | "Chester"]: number } = {
213 | Ernest: 36,
214 | Chester: 21,
215 | };
216 | ages["Ernest"]; // 36
217 | ages["Harvey"]; // TS Error: Property 'Harvey' does not exist ...
218 |
219 | // Exact same thing as above
220 | const ages: Record<"Ernest" | "Chester", number> = {
221 | Ernest: 36,
222 | Chester: 21,
223 | };
224 | ```
225 |
226 | ### Mapping enums
227 |
228 | ```ts
229 | const colors: Record = {
230 | [SeverityCode.CRITICAL]: "Red",
231 | [SeverityCode.WARNING]: "yellow",
232 | [SeverityCode.NORMAL]: "green",
233 | };
234 |
235 | colors[SeverityCode.WARNING]; // "yellow"
236 | colors[200]; // "yellow"
237 | ```
238 |
239 | # Generics
240 |
241 | Generics can be used to make types more flexible. They allow shared behavior for different types.
242 |
243 | ```ts
244 | interface HTTPResponse {
245 | status: number;
246 | data: T;
247 | }
248 |
249 | interface User {
250 | name: string;
251 | }
252 |
253 | // Type is { status: number, data: { name: string} }
254 | type UserHTTPResponse = HTTPResponse;
255 |
256 | // Type is { status: number, data: string }
257 | type StringHTTPResponse = HTTPResponse;
258 | ```
259 |
260 | ### extends
261 |
262 | The extends keyword can be used to limit which types are allowed to be used generically.
263 |
264 | TypeA extends TypeB means that TypeA's set of values is contained within TypeB's.
265 |
266 | ```ts
267 | const getUser = (id: T): { id: T; name: string } => {
268 | return fetchUser(id);
269 | };
270 |
271 | // TS Error: Argument of type 'boolean' is not assignable to string | number
272 | getUser(true);
273 |
274 | getUser(123); // Return type is { id: number, name: string }
275 | getUser("abc"); // Return type is { id: string, name: string }
276 | ```
277 |
278 | # Conditional types
279 |
280 | TypeScript supports conditional types inspired by JavaScript's ternary operator. You use _extends_ as an assertion.
281 |
282 | ```ts
283 | type BooleanFromString = T extends "true" ? true : false;
284 |
285 | type Boolean1 = BooleanFromString<"true">; // Type is true
286 | type Boolean2 = BooleanFromString<"false">; // Type is false
287 | type Boolean3 = BooleanFromString<"true" | "false">; // Type is boolean
288 | ```
289 |
290 | You can also use conditional types for the return type of a function.
291 |
292 | # typeof
293 |
294 | The _typeof_ keyword acts very differently depending on if it's in a JavaScript context or a TypeScript context. Reason being that TypeScript types don't exist when code is running.
295 |
296 | ```ts
297 | const pet = getRandomPet();
298 |
299 | // TypeScript context
300 | // (Line does not exist when code is running)
301 | type PetType = typeof pet; // Type is Cat | Dog
302 |
303 | // JavaScript context
304 | console.log(typeof pet); // Prints "object"
305 | ```
306 |
307 | The only types that exist at runtime are: `bigint, boolean, function, number, object, string, symbol, undefined.`
308 |
309 | # keyof
310 |
311 | _keyof_ allows you to convert an object type into a union of its keys.
312 |
313 | ```ts
314 | interface User {
315 | name: string;
316 | age: number;
317 | }
318 |
319 | type UserKeys = keyof User; // Type is same as "name" | "age"
320 | ```
321 |
322 | ```ts
323 | const select = (data: T, key: keyof T) => {
324 | return data[key];
325 | };
326 |
327 | const user = { name: "Alice", age: 24 };
328 |
329 | select(user, "age"); // Type is number
330 | select(user, "height"); // TS Error: "height" is not assignable to "age" | "name"
331 | ```
332 |
333 | # Template literal types
334 |
335 | ```ts
336 | type Drink = "tea" | "coffee";
337 | type Size = "S" | "M" | "L";
338 |
339 | // Type is "S-tea" | "S-coffee" | "M-tea" | "M-coffee" | "L-tea" | "L-coffee"
340 | type DrinkVariant = `${Size}-${Drink}`;
341 | ```
342 |
343 | Idea: Create an Index signature to map DrinkVariants to prices
344 |
345 | # Tuples
346 |
347 | ```ts
348 | type Coordinates = [number, number];
349 |
350 | const c1: Coordinates = [5, 12];
351 | const c2: Coordinates = [4]; // TS error: Has 1 element but requires 2
352 | const c3: Coordinates = [7, "9"]; // TS error: string is not assignable to type number
353 | ```
354 |
355 | ### Spread tuples
356 |
357 | ```ts
358 | // A person can be called multiple names but always has at least one
359 | type Names = [string, ...string[]];
360 |
361 | const p1Names: Names = ["Albert", "Bert", "Al"];
362 | const p2Names: Names = []; // TS error: Has 0 elements but requires 1
363 | ```
364 |
365 | # readonly
366 |
367 | You can use the _readonly_ modifier to prevent values from being mutated in unexpected ways.
368 |
369 | ```ts
370 | const printArray = (array: readonly number[]) => {
371 | array[0] = 100; // TS error: only reading is permitted
372 | };
373 |
374 | const array = [1, 2, 3];
375 | // We know for sure the function won't mutate our array
376 | printArray(array);
377 | ```
378 |
379 | # Useful global utility types
380 |
381 | **Partial**
382 |
383 | ```ts
384 | // Make all properties in T optional
385 | type Partial = {
386 | [P in keyof T]?: T[P];
387 | };
388 |
389 | type Example = Partial<{ name: string; age: number }>;
390 | // { name?: string | undefined, age?: number | undefined }
391 | ```
392 |
393 | **Required**
394 |
395 | ```ts
396 | // Make all properties in T required
397 | type Required = {
398 | [P in keyof T]-?: T[P];
399 | };
400 |
401 | type Example = Required<{ name?: string; age?: number }>;
402 | // { name: string, age: number }
403 | ```
404 |
405 | **Pick**
406 |
407 | ```ts
408 | // Keep only the properties in T which are in the union K
409 | type Pick = {
410 | [P in K]: T[P];
411 | };
412 |
413 | type Example = Pick<{ id: string; name: string; age: number }, "id" | "name">;
414 | // { id: string, name: string }
415 | ```
416 |
417 | **Extract**
418 |
419 | ```ts
420 | // Extract from union T what is also in union U
421 | type Extract = T extends U ? T : never;
422 |
423 | type Example = Extract<"a" | "b" | "c", "a" | "c">;
424 | // "a" | "c"
425 | ```
426 |
427 | **Exclude**
428 |
429 | ```ts
430 | // Exclude from union T what is also in union U
431 | type Exclude = T extends U ? never : T;
432 |
433 | type Example = Exclude<"a" | "b" | "c", "a" | "c">;
434 | // "b"
435 | ```
436 |
437 | **Omit**
438 |
439 | ```ts
440 | // Keep only the properties in T which share no keys with union K
441 | type Omit = Pick>;
442 |
443 | type Example = Omit<{ id: string; name: string; age: number }, "id" | "age">;
444 | // { name: string }
445 | ```
446 |
447 | **NonNullable**
448 |
449 | ```ts
450 | // Exclude null and undefined from T
451 | type NonNullable = T extends null | undefined ? never : T;
452 |
453 | type Example = NonNullable;
454 | // string
455 | ```
456 |
457 | **Others**
458 |
459 | Other global utility types not listed include:
460 |
461 | ```
462 | Parameters, ConstructorParameters, ReturnType, InstanceType, Uppercase, Lowercase, Capitalize, Uncapitalize, ThisType, Readonly, ArrayLike, Awaited, Promise, PromiseLike
463 | ```
464 |
--------------------------------------------------------------------------------
/cheat-sheet.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/cheat-sheet.pdf
--------------------------------------------------------------------------------
/cheat-sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/cheat-sheet.png
--------------------------------------------------------------------------------
/images/error-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/images/error-message.png
--------------------------------------------------------------------------------
/images/intersections-venn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/images/intersections-venn.png
--------------------------------------------------------------------------------
/images/sets-venn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/images/sets-venn.png
--------------------------------------------------------------------------------
/images/union-venn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/images/union-venn.png
--------------------------------------------------------------------------------
/small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carltheperson/magic-typescript/f6ee377c1023a7eaacf011d7a7e9d2a1fdc0e8c5/small.png
--------------------------------------------------------------------------------