├── .editorconfig ├── .gitignore ├── .prettierrc ├── 01-the-optional-chaining-operator ├── index.ts └── tsconfig.json ├── 02-the-nullish-coalescing-operator ├── index.ts └── tsconfig.json ├── 03-the-unknown-type ├── index.ts └── tsconfig.json ├── 04-assertion-functions-narrow-unknown ├── index.ts └── tsconfig.json ├── 05-assertion-functions-null-checks ├── index.ts └── tsconfig.json ├── 06-the-private-modifier ├── index.ts └── tsconfig.json ├── 07-truly-private-class-fields ├── index.ts └── tsconfig.json ├── 08-truly-private-variables-in-closures ├── index.ts └── tsconfig.json ├── 09-strict-class-property-initialization ├── index.ts └── tsconfig.json ├── 10-read-only-array-types ├── index.ts └── tsconfig.json ├── 11-read-only-tuple-types ├── swap.ts └── tsconfig.json ├── 12-const-assertions-object-literals ├── index.ts └── tsconfig.json ├── 13-const-assertions-array-literals ├── index.ts └── tsconfig.json ├── 14-const-assertions-enum-style-objects ├── index.ts └── tsconfig.json ├── 15-the-nonnullish-conditional-type ├── index.ts └── tsconfig.json ├── 16-the-returntypeof-conditional-type ├── index.ts └── tsconfig.json ├── 17-basic-template-literal-types ├── index.ts └── tsconfig.json ├── 18-advanced-template-literal-types ├── index.ts └── tsconfig.json └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.ts] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | tab_width = 2 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "printWidth": 80, 4 | "tabWidth": 2 5 | } 6 | -------------------------------------------------------------------------------- /01-the-optional-chaining-operator/index.ts: -------------------------------------------------------------------------------- 1 | type SerializationOptions = { 2 | formatting?: { 3 | getIndent?: () => number; 4 | }; 5 | }; 6 | 7 | function serializeJSON(value: any, options?: SerializationOptions) { 8 | const indent = options?.formatting?.getIndent?.(); 9 | return JSON.stringify(value, null, indent); 10 | } 11 | 12 | const user = { 13 | name: "Marius Schulz", 14 | twitter: "mariusschulz", 15 | }; 16 | 17 | const json = serializeJSON(user, { 18 | formatting: { 19 | getIndent: () => 2, 20 | }, 21 | }); 22 | 23 | console.log(json); 24 | -------------------------------------------------------------------------------- /01-the-optional-chaining-operator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /02-the-nullish-coalescing-operator/index.ts: -------------------------------------------------------------------------------- 1 | type SerializationOptions = { 2 | formatting?: { 3 | indent?: number; 4 | }; 5 | }; 6 | 7 | function serializeJSON(value: any, options?: SerializationOptions) { 8 | const indent = options?.formatting?.indent ?? 2; 9 | return JSON.stringify(value, null, indent); 10 | } 11 | 12 | const user = { 13 | name: "Marius Schulz", 14 | twitter: "mariusschulz", 15 | }; 16 | 17 | const json = serializeJSON(user, { 18 | formatting: { 19 | indent: 4, 20 | }, 21 | }); 22 | 23 | console.log(json); 24 | -------------------------------------------------------------------------------- /02-the-nullish-coalescing-operator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /03-the-unknown-type/index.ts: -------------------------------------------------------------------------------- 1 | function range(from: number, to: number): number[]; 2 | function range(from: unknown, to: unknown): number[] { 3 | if (typeof from !== "number" || typeof to !== "number") { 4 | throw Error("range() expects exactly 2 numbers"); 5 | } 6 | 7 | const values: number[] = []; 8 | for (let i = from; i < to; i++) { 9 | values.push(i); 10 | } 11 | return values; 12 | } 13 | 14 | console.log(range(0, 5)); 15 | -------------------------------------------------------------------------------- /03-the-unknown-type/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /04-assertion-functions-narrow-unknown/index.ts: -------------------------------------------------------------------------------- 1 | function assertIsNumber(value: unknown, name: string): asserts value is number { 2 | if (typeof value !== "number") { 3 | throw Error(`Expected "${name}" to be a number`); 4 | } 5 | } 6 | 7 | function range(from: number, to: number): number[]; 8 | function range(from: unknown, to: unknown): number[] { 9 | assertIsNumber(from, "from"); 10 | assertIsNumber(to, "to"); 11 | 12 | const values: number[] = []; 13 | for (let i = from; i < to; i++) { 14 | values.push(i); 15 | } 16 | return values; 17 | } 18 | 19 | console.log(range("A" as any, "F" as any)); 20 | -------------------------------------------------------------------------------- /04-assertion-functions-narrow-unknown/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /05-assertion-functions-null-checks/index.ts: -------------------------------------------------------------------------------- 1 | function assertIsNonNullish( 2 | value: T, 3 | message: string 4 | ): asserts value is NonNullable { 5 | if (value === null || value === undefined) { 6 | throw Error(message); 7 | } 8 | } 9 | 10 | const root = document.getElementById("root"); 11 | assertIsNonNullish(root, "Couldn't find DOM element #root"); 12 | 13 | root.addEventListener("click", e => { 14 | // ... 15 | }); 16 | -------------------------------------------------------------------------------- /05-assertion-functions-null-checks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /06-the-private-modifier/index.ts: -------------------------------------------------------------------------------- 1 | class Counter { 2 | private _value = 0; 3 | 4 | increment() { 5 | this._value += 1; 6 | } 7 | 8 | get count() { 9 | return this._value; 10 | } 11 | } 12 | 13 | const counter = new Counter(); 14 | counter.increment(); 15 | counter.increment(); 16 | counter.increment(); 17 | 18 | counter["_value"] = -100; 19 | 20 | console.log(counter.count); 21 | -------------------------------------------------------------------------------- /06-the-private-modifier/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /07-truly-private-class-fields/index.ts: -------------------------------------------------------------------------------- 1 | class Counter { 2 | #count = 0; 3 | 4 | increment() { 5 | this.#count += 1; 6 | } 7 | 8 | get count() { 9 | return this.#count; 10 | } 11 | } 12 | 13 | const counter = new Counter(); 14 | counter.increment(); 15 | counter.increment(); 16 | counter.increment(); 17 | 18 | console.log(counter.count); 19 | -------------------------------------------------------------------------------- /07-truly-private-class-fields/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /08-truly-private-variables-in-closures/index.ts: -------------------------------------------------------------------------------- 1 | function createCounter() { 2 | let value = 0; 3 | return { 4 | increment() { 5 | value += 1; 6 | }, 7 | get count() { 8 | return value; 9 | }, 10 | }; 11 | } 12 | 13 | const counter = createCounter(); 14 | counter.increment(); 15 | counter.increment(); 16 | counter.increment(); 17 | 18 | console.log(counter.count); 19 | -------------------------------------------------------------------------------- /08-truly-private-variables-in-closures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /09-strict-class-property-initialization/index.ts: -------------------------------------------------------------------------------- 1 | class User { 2 | username: string; 3 | 4 | constructor(username: string) { 5 | this.username = username; 6 | } 7 | 8 | setUsername(username: string) { 9 | this.username = username; 10 | } 11 | } 12 | 13 | const user = new User("mariusschulz"); 14 | const username = user.username.toLowerCase(); 15 | 16 | console.log(`Username: ${username}`); 17 | -------------------------------------------------------------------------------- /09-strict-class-property-initialization/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /10-read-only-array-types/index.ts: -------------------------------------------------------------------------------- 1 | function intersperse(array: readonly T[], separator: T): T[] { 2 | const newArray: T[] = []; 3 | for (const element of array) { 4 | if (newArray.length !== 0) { 5 | newArray.push(separator); 6 | } 7 | newArray.push(element); 8 | } 9 | return newArray; 10 | } 11 | 12 | const values: readonly string[] = ["A", "B", "C"]; 13 | const valuesWithSeparators = intersperse(values, "X"); 14 | 15 | console.log(values); 16 | console.log(valuesWithSeparators); 17 | -------------------------------------------------------------------------------- /10-read-only-array-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /11-read-only-tuple-types/swap.ts: -------------------------------------------------------------------------------- 1 | function swap(tuple: readonly [T, U]): [U, T] { 2 | const [first, second] = tuple; 3 | return [second, first]; 4 | } 5 | 6 | const keyValuePair: readonly [number, string] = [1, "one"]; 7 | const valueKeyPair = swap(keyValuePair); 8 | 9 | console.log(keyValuePair); 10 | console.log(valueKeyPair); 11 | -------------------------------------------------------------------------------- /11-read-only-tuple-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /12-const-assertions-object-literals/index.ts: -------------------------------------------------------------------------------- 1 | { 2 | const ORIGIN = { 3 | x: 0, 4 | y: 0, 5 | }; 6 | ORIGIN.x = 1; 7 | } 8 | 9 | // ============================================================================= 10 | 11 | { 12 | const ORIGIN: { 13 | readonly x: number; 14 | readonly y: number; 15 | } = { 16 | x: 0, 17 | y: 0, 18 | }; 19 | ORIGIN.x = 1; 20 | } 21 | 22 | // ============================================================================= 23 | 24 | { 25 | const ORIGIN: { 26 | readonly x: 0; 27 | readonly y: 0; 28 | } = { 29 | x: 0, 30 | y: 0, 31 | }; 32 | ORIGIN.x = 1; 33 | } 34 | 35 | // ============================================================================= 36 | 37 | { 38 | const ORIGIN_ = { 39 | x: 0, 40 | y: 0, 41 | } as const; 42 | ORIGIN_.x = 1; 43 | } 44 | -------------------------------------------------------------------------------- /12-const-assertions-object-literals/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /13-const-assertions-array-literals/index.ts: -------------------------------------------------------------------------------- 1 | { 2 | const ORIGIN = [0, 0]; 3 | ORIGIN[0] = 1; 4 | } 5 | 6 | // ============================================================================= 7 | 8 | { 9 | const ORIGIN: readonly [number, number] = [0, 0]; 10 | ORIGIN[0] = 1; 11 | } 12 | 13 | // ============================================================================= 14 | 15 | { 16 | const ORIGIN: readonly [0, 0] = [0, 0]; 17 | ORIGIN[0] = 1; 18 | } 19 | 20 | // ============================================================================= 21 | 22 | { 23 | const ORIGIN = [0, 0] as const; 24 | ORIGIN[0] = 1; 25 | } 26 | -------------------------------------------------------------------------------- /13-const-assertions-array-literals/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /14-const-assertions-enum-style-objects/index.ts: -------------------------------------------------------------------------------- 1 | // const HTTPRequestMethod = { 2 | // GET: "GET", 3 | // POST: "POST", 4 | // }; 5 | 6 | const HTTPRequestMethod = { 7 | GET: "GET", 8 | POST: "POST", 9 | } as const; 10 | 11 | type ValuesOf = T[keyof T]; 12 | type HTTPRequestMethodType = ValuesOf; 13 | 14 | // async function fetchJSON(url: string, method: "GET" | "POST") { 15 | async function fetchJSON(url: string, method: HTTPRequestMethodType) { 16 | const response = await fetch(url, { method }); 17 | return response.json(); 18 | } 19 | 20 | fetchJSON("https://example.com/", HTTPRequestMethod.GET).then(data => { 21 | // ... 22 | }); 23 | -------------------------------------------------------------------------------- /14-const-assertions-enum-style-objects/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /15-the-nonnullish-conditional-type/index.ts: -------------------------------------------------------------------------------- 1 | type NonNullish = T extends null | undefined ? never : T; 2 | 3 | type A = NonNullish; 4 | type B = NonNullish; 5 | type C = NonNullish; 6 | type D = NonNullish; 7 | type E = NonNullish; 8 | 9 | // ============================================================================= 10 | 11 | type EmailAddress = string | string[] | null | undefined; 12 | 13 | type NonNullableEmailAddress = NonNullable; 14 | 15 | type NonNullableEmailAddress1 = NonNullable< 16 | string | string[] | null | undefined 17 | >; 18 | 19 | type NonNullableEmailAddress2 = 20 | | NonNullable 21 | | NonNullable 22 | | NonNullable 23 | | NonNullable; 24 | 25 | type NonNullableEmailAddress3 = 26 | | (string extends null | undefined ? never : string) 27 | | (string[] extends null | undefined ? never : string[]) 28 | | (null extends null | undefined ? never : null) 29 | | (undefined extends null | undefined ? never : undefined); 30 | 31 | type NonNullableEmailAddress4 = string | string[] | never | never; 32 | 33 | type NonNullableEmailAddress5 = string | string[]; 34 | -------------------------------------------------------------------------------- /15-the-nonnullish-conditional-type/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /16-the-returntypeof-conditional-type/index.ts: -------------------------------------------------------------------------------- 1 | type ReturnTypeOf any> = T extends ( 2 | ...args: any 3 | ) => infer R 4 | ? R 5 | : any; 6 | 7 | type A = ReturnTypeOf<() => string>; 8 | type B = ReturnTypeOf; 9 | type C = ReturnTypeOf; 10 | type D = ReturnTypeOf; 11 | -------------------------------------------------------------------------------- /16-the-returntypeof-conditional-type/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /17-basic-template-literal-types/index.ts: -------------------------------------------------------------------------------- 1 | type MarginProperty = 2 | | "margin-block-start" 3 | | "margin-block-end" 4 | | "margin-inline-start" 5 | | "margin-inline-end"; 6 | 7 | type MarginUnit = "px" | "vh" | "vw"; 8 | type MarginValue = `${number}${MarginUnit}`; 9 | type MarginDeclaration = [MarginProperty, MarginValue]; 10 | 11 | const margin: MarginDeclaration = ["margin-block-start", "20vh"]; 12 | -------------------------------------------------------------------------------- /17-basic-template-literal-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /18-advanced-template-literal-types/index.ts: -------------------------------------------------------------------------------- 1 | function createGetterObject>( 2 | obj: TObj 3 | ): PropGetters { 4 | const newObj: any = {}; 5 | for (const key of Object.keys(obj)) { 6 | const capitalizedKey = key[0].toUpperCase() + key.substr(1); 7 | const getterKey = `get${capitalizedKey}`; 8 | newObj[getterKey] = () => obj[key]; 9 | } 10 | return newObj; 11 | } 12 | 13 | type PropGetters> = { 14 | [TKey in string & keyof TObj as `get${Capitalize}`]: () => TObj[TKey]; 15 | }; 16 | 17 | const user = createGetterObject({ 18 | name: "Marius Schulz", 19 | twitter: "mariusschulz", 20 | }); 21 | 22 | console.log(user); 23 | console.log(user.getName()); 24 | console.log(user.getTwitter()); 25 | -------------------------------------------------------------------------------- /18-advanced-template-literal-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "es2015" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced TypeScript Fundamentals 2 | 3 | This repository contains the code for my [Advanced TypeScript Fundamentals](https://egghead.io/courses/advanced-typescript-fundamentals-579c174f) video course. 4 | 5 | 1. [Use TypeScript's Optional Chaining Operator](https://egghead.io/lessons/typescript-use-typescript-s-optional-chaining-operator) 6 | 2. [Use TypeScript's Nullish Coalescing Operator](https://egghead.io/lessons/typescript-use-typescript-s-nullish-coalescing-operator) 7 | 3. [Statically Type Unknown Values with TypeScript's `unknown` Type](https://egghead.io/lessons/typescript-statically-type-unknown-values-with-typescript-s-unknown-type) 8 | 4. [Narrow Types with TypeScript's Assertion Functions](https://egghead.io/lessons/typescript-narrow-types-with-typescript-s-assertion-functions) 9 | 5. [Implement TypeScript's Truly Private Class Fields](https://egghead.io/lessons/typescript-implement-typescript-s-truly-private-class-fields) 10 | 6. [Ensure Initialization of Class Instance Properties in TypeScript](https://egghead.io/lessons/typescript-ensure-initialization-of-class-instance-properties-in-typescript) 11 | 7. [Declare TypeScript's Read-Only Array and Tuple Types](https://egghead.io/lessons/typescript-declare-typescript-s-read-only-array-and-tuple-types) 12 | 8. [Prevent Type Widening with Const Assertions in TypeScript](https://egghead.io/lessons/typescript-prevent-type-widening-with-const-assertions-in-typescript) 13 | 9. [Select One of Several Types with Conditional Types in TypeScript](https://egghead.io/lessons/typescript-select-one-of-several-types-with-conditional-types-in-typescript) 14 | 10. [Statically Type String Literals with Template Literal Types in TypeScript](https://egghead.io/lessons/typescript-statically-type-string-literals-with-template-literal-types-in-typescript) 15 | 16 | --------------------------------------------------------------------------------