├── README.md ├── 08. modules ├── 05. commonjs-syntax │ ├── main.ts │ └── math.ts ├── 02. es-module-syntax │ ├── hello.ts │ ├── main.ts │ └── maths.ts ├── 04. ts-specific-import │ ├── main.ts │ └── animal.ts ├── 01. non-modules │ └── main.ts └── 03. additional-import-syntax │ ├── maths.ts │ └── main.ts ├── 01. basics ├── 01. hello-world │ └── greeter.ts ├── 02. static-type-checking │ └── main.ts ├── 07. strict-null-checks │ └── main.ts ├── 05. downleveling │ ├── main.js │ └── main.ts ├── 03. non-exception-failures │ ├── main.ts │ └── legitimate-bugs │ │ ├── uncalledFunctions.ts │ │ ├── logicErrors.ts │ │ └── typos.ts ├── 06. no-implicit-any │ └── main.ts ├── 04. explicit-types │ └── main.ts ├── 08. non-null-assertion-operator │ └── main.ts └── 09. definite-assignment-assertion-operator │ └── main.ts ├── 02. types ├── 02. functions │ ├── return-type │ │ └── main.ts │ └── parameter-type │ │ └── main.ts ├── 05. optional-parameters │ └── main.ts ├── 04. object-types │ └── main.ts ├── 08. interfaces │ └── main.ts ├── 01. any │ └── main.ts ├── 06. union-types │ └── main.ts ├── 07. type-aliases │ └── main.ts ├── 10. literal-types │ └── main.ts ├── 09. type-assertions │ └── main.ts ├── 03. anonymous-functions │ └── main.ts └── 11. literal-inference │ └── main.ts ├── 04. functions ├── 15. Function-function-types │ └── main.ts ├── 12. void-functions │ └── main.ts ├── 02. type-aliases-functions │ └── main.ts ├── 16. rest-parameters │ └── main.ts ├── 03. call-signatures │ └── main.ts ├── 01. function-type-expressions │ └── main.ts ├── 04. contruct-signatures │ └── main.ts ├── 06. inference │ └── main.ts ├── 08. type-arguments │ └── main.ts ├── 18. function-assignability │ └── main.ts ├── 11. declaring-this-functions │ └── main.ts ├── 14. never-function-types │ └── main.ts ├── 09. optional-parameters │ └── main.ts ├── 17. parameter-destructing │ └── main.ts ├── 05. generic-functions │ └── main.ts ├── 13. unknown-function-types │ └── main.ts ├── 07. constraints │ └── main.ts └── 10. function-overloads │ └── main.ts ├── 07. classes ├── 07. generic-classes │ └── main.ts ├── 03. index-signatures │ └── main.ts ├── 06. static-members │ └── main.ts ├── 01. constructors │ └── main.ts ├── 09. this-types │ └── main.ts ├── 12. class-expressions │ └── main.ts ├── 14. relationships-between-classes │ └── main.ts ├── 08. this-parameters │ └── main.ts ├── 11. parameter-properties │ └── main.ts ├── 05. member-visibility │ └── main.ts ├── 04. extends-clause │ └── main.ts ├── 13. abstract-class-members │ └── main.ts ├── 02. getters-and-setters │ └── main.ts └── 10. this-based-type-guards │ └── main.ts ├── 05. object-types ├── 08. Array-type │ └── main.ts ├── 06. interfaces-intersections-types │ └── main.ts ├── 05. intersection-types │ └── main.ts ├── 04. extending-types │ └── main.ts ├── 03. index-signatures │ └── main.ts ├── 09. ReadOnlyArray-type │ └── main.ts ├── 07. generic-object-types │ └── main.ts ├── 02. readonly-properties │ └── main.ts ├── 10. tuple-types │ └── main.ts └── 01.optional-properties │ └── main.ts ├── 09. utility-types ├── 09. nonnullable-type │ └── main.ts ├── 08. extract-type │ └── main.ts ├── 14. this-parameter-type │ └── main.ts ├── 02. required-type │ └── main.ts ├── 05. pick-type │ └── main.ts ├── 07. exclude-type │ └── main.ts ├── 03. readonly-type │ └── main.ts ├── 04. record-type │ └── main.ts ├── 01. partial-type │ └── main.ts ├── 06. omit-type │ └── main.ts ├── 13. instance-type │ └── main.ts ├── 11. contructor-parameter-type │ └── main.ts ├── 10. parameter-type │ └── main.ts └── 12. return-type │ └── main.ts ├── 03. narrowing ├── 06. assignments-narrowing │ └── main.ts ├── 05. instanceof-narrowing │ └── main.ts ├── 07. control-flow-narrowing │ └── main.ts ├── 08. type-predicate-narrowing │ └── main.ts ├── 04. in-operator-narrowing │ └── main.ts ├── 02. truthiness-narrowing │ └── main.ts ├── 10. exhaustiveness-checking-narrowing │ └── main.ts ├── 03. equality-narrowing │ └── main.ts ├── 01. type-guards-narrowing │ └── main.ts └── 09. discriminated-unions-narrowing │ └── main.ts ├── 06. type-manipulation ├── 01. keyof-operator │ └── main.ts ├── 05. distributive-conditional-types │ └── main.ts ├── 02. typeof-operator │ └── main.ts ├── 09. template-literal-types │ └── main.ts ├── 07. mapping-modifiers │ └── main.ts ├── 10. inference-with-literals │ └── main.ts ├── 06. mapped-types │ └── main.ts ├── 03. indexed-access-types │ └── main.ts ├── 08. key-remapping │ └── main.ts └── 04. conditional-types │ └── main.ts ├── LICENSE └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # ts-hub 2 | A hub for Typescript Advanced concepts. 3 | -------------------------------------------------------------------------------- /08. modules/05. commonjs-syntax/main.ts: -------------------------------------------------------------------------------- 1 | const maths = require("maths"); 2 | maths.pi; -------------------------------------------------------------------------------- /01. basics/01. hello-world/greeter.ts: -------------------------------------------------------------------------------- 1 | const message: string = 'Hello World!'; 2 | 3 | console.log(message); -------------------------------------------------------------------------------- /02. types/02. functions/return-type/main.ts: -------------------------------------------------------------------------------- 1 | function getFavoriteNumber(): number { 2 | return 26; 3 | } -------------------------------------------------------------------------------- /04. functions/15. Function-function-types/main.ts: -------------------------------------------------------------------------------- 1 | 2 | function doSomething(f: Function) { 3 | f(1, 2, 3); 4 | } -------------------------------------------------------------------------------- /04. functions/12. void-functions/main.ts: -------------------------------------------------------------------------------- 1 | // The inferred return type is void 2 | function noop() { 3 | return; 4 | } -------------------------------------------------------------------------------- /08. modules/02. es-module-syntax/hello.ts: -------------------------------------------------------------------------------- 1 | export default function helloWorld() { 2 | console.log("Hello, world!"); 3 | } -------------------------------------------------------------------------------- /08. modules/04. ts-specific-import/main.ts: -------------------------------------------------------------------------------- 1 | import type { Cat, Dog } from "./animal"; 2 | export type Animals = Cat | Dog; -------------------------------------------------------------------------------- /04. functions/02. type-aliases-functions/main.ts: -------------------------------------------------------------------------------- 1 | type GreetFunction = (a: string) => void; 2 | function greeter(fn: GreetFunction) { 3 | // ... 4 | } -------------------------------------------------------------------------------- /01. basics/02. static-type-checking/main.ts: -------------------------------------------------------------------------------- 1 | const message = 'TS'; 2 | 3 | message(); 4 | // This expression is not callable. Type 'String' has no call signatures. -------------------------------------------------------------------------------- /07. classes/07. generic-classes/main.ts: -------------------------------------------------------------------------------- 1 | class Box { 2 | contents: Type; 3 | constructor(value: Type) { 4 | this.contents = value; 5 | } 6 | } -------------------------------------------------------------------------------- /01. basics/07. strict-null-checks/main.ts: -------------------------------------------------------------------------------- 1 | let foo: number = 123; 2 | foo = null; // Okay 3 | foo = undefined; // Okay 4 | 5 | let foo1 = undefined; 6 | foo1 = null; // NOT Okay -------------------------------------------------------------------------------- /08. modules/04. ts-specific-import/animal.ts: -------------------------------------------------------------------------------- 1 | export type Cat = { breed: string; yearOfBirth: number }; 2 | 3 | export interface Dog { 4 | breeds: string[]; 5 | yearOfBirth: number; 6 | } -------------------------------------------------------------------------------- /01. basics/05. downleveling/main.js: -------------------------------------------------------------------------------- 1 | function greet(person, date) { 2 | console.log("Hello " + person + ", today is " + date.toDateString() + "!"); 3 | } 4 | greet("Maddison", new Date()); 5 | -------------------------------------------------------------------------------- /07. classes/03. index-signatures/main.ts: -------------------------------------------------------------------------------- 1 | class MyClass { 2 | [s: string]: boolean | ((s: string) => boolean); 3 | 4 | check(s: string) { 5 | return this[s] as boolean; 6 | } 7 | } -------------------------------------------------------------------------------- /07. classes/06. static-members/main.ts: -------------------------------------------------------------------------------- 1 | class MyClass { 2 | static x = 0; 3 | static printX() { 4 | console.log(MyClass.x); 5 | } 6 | 7 | } 8 | console.log(MyClass.x); 9 | MyClass.printX();} -------------------------------------------------------------------------------- /08. modules/02. es-module-syntax/main.ts: -------------------------------------------------------------------------------- 1 | import hello from "./hello"; 2 | import { pi, phi, absolute } from "./maths.js"; 3 | 4 | 5 | 6 | hello(); 7 | 8 | console.log(pi); 9 | const absPhi = absolute(phi); -------------------------------------------------------------------------------- /01. basics/03. non-exception-failures/main.ts: -------------------------------------------------------------------------------- 1 | const user = { 2 | name: 'user', 3 | age: 26 4 | }; 5 | 6 | user.location; 7 | // Property 'location' does not exist on type '{ name: string; age: number; }'. -------------------------------------------------------------------------------- /04. functions/16. rest-parameters/main.ts: -------------------------------------------------------------------------------- 1 | function multiply(n: number, ...m: number[]) { 2 | return m.map((x) => n * x); 3 | } 4 | // 'a' gets value [10, 20, 30, 40] 5 | const a = multiply(10, 1, 2, 3, 4); -------------------------------------------------------------------------------- /07. classes/01. constructors/main.ts: -------------------------------------------------------------------------------- 1 | class Point { 2 | // Overloads 3 | constructor(x: number, y: string); 4 | constructor(s: string); 5 | constructor(xs: any, y?: any) { 6 | // TBD 7 | } 8 | } -------------------------------------------------------------------------------- /08. modules/01. non-modules/main.ts: -------------------------------------------------------------------------------- 1 | class Point1 { 2 | x = 0; 3 | y = 0; 4 | } 5 | 6 | class Point2 { 7 | x = 0; 8 | y = 0; 9 | } 10 | 11 | // OK 12 | const p: Point1 = new Point2(); -------------------------------------------------------------------------------- /07. classes/09. this-types/main.ts: -------------------------------------------------------------------------------- 1 | class Box { 2 | contents: string = ""; 3 | set(value: string) { 4 | // (method) Box.set(value: string): this 5 | this.contents = value; 6 | return this; 7 | } 8 | } -------------------------------------------------------------------------------- /02. types/05. optional-parameters/main.ts: -------------------------------------------------------------------------------- 1 | function printName(obj: { first: string; last?: string }) { 2 | // ... 3 | } 4 | // Both OK 5 | printName({ first: "Bob" }); 6 | printName({ first: "Alice", last: "Alisson" }); -------------------------------------------------------------------------------- /08. modules/03. additional-import-syntax/maths.ts: -------------------------------------------------------------------------------- 1 | export const pi = 3.14; 2 | export default class RandomNumberGenerator {} 3 | export function absolute(num: number) { 4 | if (num < 0) return num * -1; 5 | return num; 6 | } -------------------------------------------------------------------------------- /07. classes/12. class-expressions/main.ts: -------------------------------------------------------------------------------- 1 | const someClass = class { 2 | content: Type; 3 | constructor(value: Type) { 4 | this.content = value; 5 | } 6 | }; 7 | 8 | const m = new someClass("Hello, world"); -------------------------------------------------------------------------------- /07. classes/14. relationships-between-classes/main.ts: -------------------------------------------------------------------------------- 1 | class Point1 { 2 | x = 0; 3 | y = 0; 4 | } 5 | 6 | class Point2 { 7 | x = 0; 8 | y = 0; 9 | } 10 | 11 | // OK 12 | const p: Point1 = new Point2(); -------------------------------------------------------------------------------- /01. basics/03. non-exception-failures/legitimate-bugs/uncalledFunctions.ts: -------------------------------------------------------------------------------- 1 | function flipCoin() { 2 | // Meant to be Math.random() 3 | return Math.random < 0.5; 4 | // Operator '<' cannot be applied to types '() => number' and 'number'. 5 | } -------------------------------------------------------------------------------- /01. basics/06. no-implicit-any/main.ts: -------------------------------------------------------------------------------- 1 | function log(someArg: any) { 2 | sendDataToServer(someArg); 3 | } 4 | 5 | function sendDataToServer(arg: string) { 6 | } 7 | // What arg is valid and what isn't? 8 | log(123); 9 | log('hello world'); -------------------------------------------------------------------------------- /04. functions/03. call-signatures/main.ts: -------------------------------------------------------------------------------- 1 | type DescribableFunction = { 2 | description: string; 3 | (someArg: number): boolean; 4 | }; 5 | 6 | 7 | function doSomething(fn: DescribableFunction) { 8 | console.log(fn.description + " returned " + fn(6)); 9 | } -------------------------------------------------------------------------------- /08. modules/05. commonjs-syntax/math.ts: -------------------------------------------------------------------------------- 1 | function absolute(num: number) { 2 | if (num < 0) return num * -1; 3 | return num; 4 | } 5 | 6 | module.exports = { 7 | pi: 3.14, 8 | squareTwo: 1.41, 9 | phi: 1.61, 10 | absolute, 11 | }; -------------------------------------------------------------------------------- /05. object-types/08. Array-type/main.ts: -------------------------------------------------------------------------------- 1 | function doSomething(value: Array) { 2 | // ... 3 | } 4 | 5 | let myArray: string[] = ["hello", "world"]; 6 | 7 | // either of these work! 8 | doSomething(myArray); 9 | doSomething(new Array("hello", "world")); -------------------------------------------------------------------------------- /04. functions/01. function-type-expressions/main.ts: -------------------------------------------------------------------------------- 1 | function greeter(fn: (a: string) => void) { 2 | fn('Hello world') 3 | } 4 | 5 | function printToConsole(s: string) { 6 | console.log(s); 7 | } 8 | 9 | greeter(printToConsole); 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /07. classes/08. this-parameters/main.ts: -------------------------------------------------------------------------------- 1 | class MyClass { 2 | name = "MyClass"; 3 | getName(this: MyClass) { 4 | return this.name; 5 | } 6 | } 7 | const c = new MyClass(); 8 | // OK 9 | c.getName(); 10 | 11 | // Error, would crash 12 | const g = c.getName; 13 | console.log(g()); -------------------------------------------------------------------------------- /05. object-types/06. interfaces-intersections-types/main.ts: -------------------------------------------------------------------------------- 1 | // The principle difference between the two is how conflicts are handled, 2 | // and that difference is typically one of the main reasons why you’d 3 | // pick one over the other between an interface and a type alias of an intersection type. -------------------------------------------------------------------------------- /02. types/04. object-types/main.ts: -------------------------------------------------------------------------------- 1 | // The parameter's type annotation is an object type 2 | function printCoord(pt: { x: number; y: number }) { 3 | console.log("The coordinate's x value is " + pt.x); 4 | console.log("The coordinate's y value is " + pt.y); 5 | } 6 | printCoord({ x: 3, y: 7 }); -------------------------------------------------------------------------------- /08. modules/03. additional-import-syntax/main.ts: -------------------------------------------------------------------------------- 1 | import RNGen, { pi as π } from "./maths"; 2 | import * as math from "./maths"; 3 | 4 | 5 | RNGen; 6 | // (alias) class RNGen 7 | // import RNGen 8 | 9 | console.log(π); 10 | console.log(math.pi); 11 | const positivePhi = math.absolute(math.phi); -------------------------------------------------------------------------------- /04. functions/04. contruct-signatures/main.ts: -------------------------------------------------------------------------------- 1 | type SomeConstructor = { 2 | new (s: string): SomeObject; 3 | }; 4 | 5 | function fn(ctor: SomeConstructor) { 6 | return new ctor("hello"); 7 | } 8 | 9 | interface CallOrConstruct { 10 | new (s: string): Date; 11 | (n?: number): number; 12 | } -------------------------------------------------------------------------------- /04. functions/06. inference/main.ts: -------------------------------------------------------------------------------- 1 | function map(arr: Input[], func: (arg: Input) => Output): Output[] { 2 | return arr.map(func); 3 | } 4 | 5 | // Parameter 'n' is of type 'string' 6 | // 'parsed' is of type 'number[]' 7 | const parsed = map(["1", "2", "3"], (n) => parseInt(n)); -------------------------------------------------------------------------------- /01. basics/03. non-exception-failures/legitimate-bugs/logicErrors.ts: -------------------------------------------------------------------------------- 1 | const value = Math.random() < 0.5 ? "a" : "b"; 2 | if (value !== "a") { 3 | // ... 4 | } else if (value === "b") { 5 | // This condition will always return 'false' since the types '"a"' and '"b"' have no overlap. 6 | // Oops, unreachable 7 | } -------------------------------------------------------------------------------- /04. functions/08. type-arguments/main.ts: -------------------------------------------------------------------------------- 1 | function combine(arr1: Type[], arr2: Type[]): Type[] { 2 | return arr1.concat(arr2); 3 | } 4 | 5 | const arr = combine([1, 2, 3], ["hello"]); 6 | // Type 'string' is not assignable to type 'number'. 7 | 8 | const arr = combine([1, 2, 3], ["hello"]); -------------------------------------------------------------------------------- /08. modules/02. es-module-syntax/maths.ts: -------------------------------------------------------------------------------- 1 | // @filename: maths.ts 2 | export var pi = 3.14; 3 | export let squareTwo = 1.41; 4 | export const phi = 1.61; 5 | 6 | export class RandomNumberGenerator {} 7 | 8 | export function absolute(num: number) { 9 | if (num < 0) return num * -1; 10 | return num; 11 | } -------------------------------------------------------------------------------- /09. utility-types/09. nonnullable-type/main.ts: -------------------------------------------------------------------------------- 1 | // NonNullable 2 | // Constructs a type by excluding null and undefined from Type. 3 | 4 | type T0 = NonNullable; 5 | // type T0 = string | number 6 | 7 | type T1 = NonNullable; 8 | // type T1 = string[] -------------------------------------------------------------------------------- /01. basics/04. explicit-types/main.ts: -------------------------------------------------------------------------------- 1 | function greet(person: string, date: Date) { 2 | console.log(`Hello ${person}, today is ${date.toDateString()}!`); 3 | } 4 | 5 | greet("Maddison", Date()); 6 | greet("Maddison", new Date()); 7 | // Argument of type 'string' is not assignable to parameter of type 'Date'. -------------------------------------------------------------------------------- /02. types/08. interfaces/main.ts: -------------------------------------------------------------------------------- 1 | interface Pt { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | function printCoord(pt: Pt) { 7 | console.log("The coordinate's x value is " + pt.x); 8 | console.log("The coordinate's y value is " + pt.y); 9 | } 10 | 11 | printCoord({ x: 100, y: 100 }); -------------------------------------------------------------------------------- /02. types/02. functions/parameter-type/main.ts: -------------------------------------------------------------------------------- 1 | // Parameter type annotation 2 | function greet(name: string) { 3 | console.log("Hello, " + name.toUpperCase() + "!!"); 4 | } 5 | 6 | // Would be a runtime error if executed! 7 | greet(42); 8 | 9 | //Argument of type 'number' is not assignable to parameter of type 'string'. -------------------------------------------------------------------------------- /02. types/01. any/main.ts: -------------------------------------------------------------------------------- 1 | let obj: any = { x: 0 }; 2 | // None of the following lines of code will throw compiler errors. 3 | // Using `any` disables all further type checking, and it is assumed 4 | // you know the environment better than TypeScript. 5 | obj.foo(); 6 | obj(); 7 | obj.bar = 100; 8 | obj = "hello"; 9 | const n: number = obj; -------------------------------------------------------------------------------- /03. narrowing/06. assignments-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | let x = Math.random() < 0.5 ? 10 : "hello world!"; 2 | // let x: string | number 3 | 4 | x = 1; 5 | 6 | console.log(x); 7 | // let x: number 8 | 9 | x = true; 10 | // Type 'boolean' is not assignable to type 'string | number'. 11 | 12 | console.log(x); 13 | // let x: string | number -------------------------------------------------------------------------------- /09. utility-types/08. extract-type/main.ts: -------------------------------------------------------------------------------- 1 | // Extract 2 | // Constructs a type by extracting from Type all union members that are assignable to Union. 3 | 4 | 5 | type T0 = Extract<"a" | "b" | "c", "a" | "f">; 6 | // type T0 = "a" 7 | 8 | type T1 = Extract void), Function>; 9 | // type T1 = () => void -------------------------------------------------------------------------------- /03. narrowing/05. instanceof-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | function logValue(x: Date | string) { 2 | if (x instanceof Date) { 3 | console.log(x.toUTCString()); 4 | 5 | // (parameter) x: Date 6 | } else { 7 | console.log(x.toUpperCase()); 8 | 9 | // (parameter) x: string 10 | } 11 | } -------------------------------------------------------------------------------- /05. object-types/05. intersection-types/main.ts: -------------------------------------------------------------------------------- 1 | // Here, we’ve intersected Colorful and Circle to produce a new type 2 | // that has all the members of Colorful and Circle. 3 | 4 | interface Colorful { 5 | color: string; 6 | } 7 | 8 | interface Circle { 9 | radius: number; 10 | } 11 | 12 | type ColorfulCircle = Colorful & Circle; -------------------------------------------------------------------------------- /04. functions/18. function-assignability/main.ts: -------------------------------------------------------------------------------- 1 | type voidFunc = () => void; 2 | 3 | const f1: voidFunc = () => { 4 | return true; 5 | }; 6 | 7 | const f2: voidFunc = () => true; 8 | 9 | const f3: voidFunc = function () { 10 | return true; 11 | }; 12 | 13 | 14 | 15 | const v1 = f1(); 16 | 17 | const v2 = f2(); 18 | 19 | const v3 = f3(); -------------------------------------------------------------------------------- /01. basics/03. non-exception-failures/legitimate-bugs/typos.ts: -------------------------------------------------------------------------------- 1 | const announcement = 'Hello, World !'; 2 | 3 | announcement.toLocaleLowercase(); 4 | announcement.toLocalLowerCase(); 5 | // Property 'toLocaleLowercase' does not exist on type '"Hello, World !"'. Did you mean 'toLocaleLowerCase'? 6 | 7 | announcement.toLocaleLowerCase(); 8 | 9 | 10 | -------------------------------------------------------------------------------- /04. functions/11. declaring-this-functions/main.ts: -------------------------------------------------------------------------------- 1 | interface DB { 2 | filterUsers(filter: (this: User) => boolean): User[]; 3 | } 4 | 5 | const db = getDB(); 6 | 7 | const admins = db.filterUsers(function (this: User) { 8 | return this.admin; 9 | }); 10 | 11 | 12 | function getDB(): DB { 13 | throw new Error('Method unimplemented') 14 | } -------------------------------------------------------------------------------- /07. classes/11. parameter-properties/main.ts: -------------------------------------------------------------------------------- 1 | class Params { 2 | constructor( 3 | public readonly x: number, 4 | protected y: number, 5 | private z: number 6 | ) { 7 | // No body necessary 8 | } 9 | } 10 | const a = new Params(1, 2, 3); 11 | console.log(a.x); 12 | 13 | // (property) Params.x: number 14 | console.log(a.z); 15 | -------------------------------------------------------------------------------- /01. basics/08. non-null-assertion-operator/main.ts: -------------------------------------------------------------------------------- 1 | // Compiled with --strictNullChecks 2 | function validateEntity(e?: Entity) { 3 | // Throw exception if e is null or invalid entity 4 | } 5 | 6 | function processEntity(e?: Entity) { 7 | validateEntity(e); 8 | let a = e.name; // TS ERROR: e may be null. 9 | let b = e!.name; // OKAY. We are asserting that e is non-null. 10 | } -------------------------------------------------------------------------------- /09. utility-types/14. this-parameter-type/main.ts: -------------------------------------------------------------------------------- 1 | // ThisParameterType 2 | // Extracts the type of the this parameter for a function type, 3 | // or unknown if the function type has no this parameter. 4 | 5 | 6 | function toHex(this: Number) { 7 | return this.toString(16); 8 | } 9 | 10 | function numberToString(n: ThisParameterType) { 11 | return toHex.apply(n); 12 | } -------------------------------------------------------------------------------- /07. classes/05. member-visibility/main.ts: -------------------------------------------------------------------------------- 1 | class Greeter { 2 | public greet() { 3 | console.log("Hello, " + this.getName()); 4 | } 5 | protected getName() { 6 | return "hi"; 7 | } 8 | } 9 | 10 | class SpecialGreeter extends Greeter { 11 | public howdy() { 12 | // OK to access protected member here 13 | console.log("Howdy, " + this.getName()); 14 | } 15 | } -------------------------------------------------------------------------------- /09. utility-types/02. required-type/main.ts: -------------------------------------------------------------------------------- 1 | // Required 2 | // Constructs a type consisting of all properties of Type set to required. 3 | 4 | interface Props { 5 | a?: number; 6 | b?: string; 7 | } 8 | 9 | 10 | const obj:Props = {a: 5}; 11 | const obj2:Required = {a: 5, b: 'string'}; 12 | // Property 'b' is missing in type '{ a: number; }' but required in type 'Required'. 13 | -------------------------------------------------------------------------------- /02. types/06. union-types/main.ts: -------------------------------------------------------------------------------- 1 | function printId(id: number | string) { 2 | console.log("Your ID is: " + id); 3 | } 4 | // OK 5 | printId(101); 6 | // OK 7 | printId("202"); 8 | // Error 9 | printId({ myID: 22342 }); 10 | 11 | //Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'. 12 | // Type '{ myID: number; }' is not assignable to type 'number' -------------------------------------------------------------------------------- /02. types/07. type-aliases/main.ts: -------------------------------------------------------------------------------- 1 | type Point = { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | // Exactly the same as the earlier example 7 | function printCoord(pt: Point) { 8 | console.log("The coordinate's x value is " + pt.x); 9 | console.log("The coordinate's y value is " + pt.y); 10 | } 11 | 12 | printCoord({ x: 100, y: 100 }); 13 | 14 | type ID = number | string; 15 | 16 | 17 | -------------------------------------------------------------------------------- /04. functions/14. never-function-types/main.ts: -------------------------------------------------------------------------------- 1 | // Functions that never return a value 2 | 3 | function fail(msg: string): never { 4 | throw new Error(msg); 5 | } 6 | 7 | 8 | function fn(x: string | number) { 9 | if (typeof x === "string") { 10 | // do something 11 | } else if (typeof x === "number") { 12 | // do something else 13 | } else { 14 | x; // has type 'never'! 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /04. functions/09. optional-parameters/main.ts: -------------------------------------------------------------------------------- 1 | function f(n: number) { 2 | console.log(n.toFixed()); // 0 arguments 3 | console.log(n.toFixed(3)); // 1 argument 4 | } 5 | 6 | function f(x?: number) { 7 | // ... 8 | } 9 | f(); // OK 10 | f(10); // OK 11 | 12 | 13 | function f(x = 10) { 14 | // ... 15 | } 16 | 17 | declare function f(x?: number): void; 18 | // cut 19 | // All OK 20 | f(); 21 | f(10); 22 | f(undefined); -------------------------------------------------------------------------------- /02. types/10. literal-types/main.ts: -------------------------------------------------------------------------------- 1 | function printText(s: string, alignment: "left" | "right" | "center") { 2 | // ... 3 | } 4 | printText("Hello, world", "left"); 5 | printText("G'day, mate", "centre"); 6 | 7 | 8 | interface Options { 9 | width: number; 10 | } 11 | function configure(x: Options | "auto") { 12 | // ... 13 | } 14 | configure({ width: 100 }); 15 | configure("auto"); 16 | configure("automatic"); -------------------------------------------------------------------------------- /07. classes/04. extends-clause/main.ts: -------------------------------------------------------------------------------- 1 | class Base { 2 | greet() { 3 | console.log("Hello, world!"); 4 | } 5 | } 6 | 7 | class Derived extends Base { 8 | greet(name?: string) { 9 | if (name === undefined) { 10 | super.greet(); 11 | } else { 12 | console.log(`Hello, ${name.toUpperCase()}`); 13 | } 14 | } 15 | } 16 | 17 | const d = new Derived(); 18 | d.greet(); 19 | d.greet("reader"); -------------------------------------------------------------------------------- /09. utility-types/05. pick-type/main.ts: -------------------------------------------------------------------------------- 1 | // Pick 2 | // Constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type. 3 | 4 | interface Todo { 5 | title: string; 6 | description: string; 7 | completed: boolean; 8 | } 9 | 10 | type TodoPreview = Pick; 11 | 12 | const todo:TodoPreview = { 13 | title: 'Clean room', 14 | completed: false 15 | } -------------------------------------------------------------------------------- /09. utility-types/07. exclude-type/main.ts: -------------------------------------------------------------------------------- 1 | // Exclude 2 | // Constructs a type by excluding from Type all union members 3 | // that are assignable to ExcludedUnion. 4 | 5 | type T0 = Exclude<"a" | "b" | "c", "a">; 6 | // type T0 = "b" | "c" 7 | 8 | 9 | type T1 = Exclude<"a" | "b" | "c", "a" | "b">; 10 | // type T1 = "c" 11 | 12 | type T2 = Exclude void), Function>; 13 | // type T2 = string | number -------------------------------------------------------------------------------- /04. functions/17. parameter-destructing/main.ts: -------------------------------------------------------------------------------- 1 | function sum({ a, b, c }) { 2 | console.log(a + b + c); 3 | } 4 | sum({ a: 10, b: 3, c: 9 }); 5 | 6 | 7 | // Wirh parameter destructing 8 | 9 | function sum({ a, b, c }: { a: number; b: number; c: number }) { 10 | console.log(a + b + c); 11 | } 12 | // Same as prior example 13 | type ABC = { a: number; b: number; c: number }; 14 | function sum({ a, b, c }: ABC) { 15 | console.log(a + b + c); 16 | } -------------------------------------------------------------------------------- /07. classes/13. abstract-class-members/main.ts: -------------------------------------------------------------------------------- 1 | abstract class Base { 2 | abstract getName(): string; 3 | 4 | printName() { 5 | console.log("Hello, " + this.getName()); 6 | } 7 | } 8 | 9 | const b = new Base(); 10 | // Cannot create an instance of an abstract class 11 | 12 | class Derived extends Base { 13 | getName() { 14 | return "world"; 15 | } 16 | } 17 | 18 | const d = new Derived(); 19 | d.printName(); -------------------------------------------------------------------------------- /05. object-types/04. extending-types/main.ts: -------------------------------------------------------------------------------- 1 | // The extends keyword on an interface allows us to effectively 2 | // copy members from other named types, and add whatever new members we want. 3 | 4 | interface Colorful { 5 | color: string; 6 | } 7 | 8 | interface Circle { 9 | radius: number; 10 | } 11 | 12 | interface ColorfulCircle extends Colorful, Circle {} 13 | 14 | const cc: ColorfulCircle = { 15 | color: "red", 16 | radius: 42, 17 | }; 18 | -------------------------------------------------------------------------------- /09. utility-types/03. readonly-type/main.ts: -------------------------------------------------------------------------------- 1 | // Readonly 2 | // Constructs a type with all properties of Type set to readonly, 3 | // meaning the properties of the constructed type cannot be reassigned. 4 | 5 | 6 | 7 | interface Todo { 8 | title: string; 9 | } 10 | 11 | 12 | const todo:Readonly = { 13 | title: 'Delete inactive users' 14 | } 15 | 16 | todo.title = 'sd'; 17 | // Cannot assign to 'title' because it is a read-only property 18 | 19 | -------------------------------------------------------------------------------- /01. basics/05. downleveling/main.ts: -------------------------------------------------------------------------------- 1 | function greet(person: string, date: Date) { 2 | console.log(`Hello ${person}, today is ${date.toDateString()}!`); 3 | } 4 | 5 | greet("Maddison", new Date()); 6 | 7 | // when compliling with tsc 8 | // `Hello ${person}, today is ${date.toDateString()}!`; 9 | // changes to 10 | // "Hello " + person + ", today is " + date.toDateString() + "!"; 11 | 12 | 13 | // but running with tsc --target es2015 gives the same output -------------------------------------------------------------------------------- /02. types/09. type-assertions/main.ts: -------------------------------------------------------------------------------- 1 | const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement; 2 | 3 | // or 4 | 5 | const myCanvas2 = document.getElementById("main_canvas"); 6 | 7 | 8 | const x = "hello" as number; 9 | // Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. 10 | 11 | 12 | // const a = (expr as any) as T; -------------------------------------------------------------------------------- /03. narrowing/07. control-flow-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | 2 | function example() { 3 | let x: string | number | boolean; 4 | 5 | x = Math.random() < 0.5; 6 | 7 | console.log(x); 8 | 9 | let x: boolean 10 | 11 | if (Math.random() < 0.5) { 12 | x = "hello"; 13 | console.log(x); 14 | 15 | let x: string 16 | } else { 17 | x = 100; 18 | console.log(x); 19 | 20 | let x: number 21 | } 22 | 23 | return x; 24 | 25 | let x: string | number 26 | } -------------------------------------------------------------------------------- /04. functions/05. generic-functions/main.ts: -------------------------------------------------------------------------------- 1 | function firstElement(arr: Type[]): Type { 2 | return arr[0]; 3 | } 4 | 5 | // s is of type 'string' 6 | const s = firstElement(["a", "b", "c"]); 7 | // n is of type 'number' 8 | const n = firstElement([1, 2, 3]); 9 | 10 | function filter1(arr: Type[], func: (arg: Type) => boolean): Type[] { 11 | return arr.filter(func); 12 | } 13 | 14 | function filter2 boolean>( 15 | arr: Type[], 16 | func: Func 17 | ): Type[] { 18 | return arr.filter(func); 19 | } -------------------------------------------------------------------------------- /02. types/03. anonymous-functions/main.ts: -------------------------------------------------------------------------------- 1 | // No type annotations here, but TypeScript can spot the bug 2 | const names = ["Alice", "Bob", "Eve"]; 3 | 4 | // Contextual typing for function 5 | names.forEach(function (s) { 6 | console.log(s.toUppercase()); 7 | // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'? 8 | }); 9 | 10 | // Contextual typing also applies to arrow functions 11 | names.forEach((s) => { 12 | console.log(s.toUppercase()); 13 | // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'? 14 | }); -------------------------------------------------------------------------------- /06. type-manipulation/01. keyof-operator/main.ts: -------------------------------------------------------------------------------- 1 | // The keyof operator takes an object type and produces a string or 2 | // numeric literal union of its keys 3 | 4 | 5 | type Point = { x: number; y: number }; 6 | type P = keyof Point; 7 | 8 | 9 | // If the type has a string or number index signature, 10 | // keyof will return those types instead: 11 | 12 | type Arrayish = { [n: number]: unknown }; 13 | type A = keyof Arrayish; 14 | // type A = number 15 | 16 | 17 | type Mapish = { [k: string]: boolean }; 18 | type M = keyof Mapish; 19 | // type M = string | number 20 | -------------------------------------------------------------------------------- /09. utility-types/04. record-type/main.ts: -------------------------------------------------------------------------------- 1 | // Record 2 | // Constructs an object type whose property keys are Keys and whose property values are Type. 3 | // This utility can be used to map the properties of a type to another type. 4 | 5 | 6 | interface CatInfo { 7 | age: number; 8 | breed: string; 9 | } 10 | 11 | type CatName = 'miffy' | 'boris' | 'mordred'; 12 | 13 | const cats: Record = { 14 | miffy: { age: 10, breed: "Persian" }, 15 | boris: { age: 5, breed: "Maine Coon" }, 16 | mordred: { age: 16, breed: "British Shorthair" }, 17 | }; -------------------------------------------------------------------------------- /09. utility-types/01. partial-type/main.ts: -------------------------------------------------------------------------------- 1 | // Partial 2 | // Constructs a type with all properties of Type set to optional. 3 | // This utility will return a type that represents all subsets of a given type. 4 | 5 | 6 | interface Todo { 7 | title: string; 8 | description: string; 9 | } 10 | 11 | function updateTodo(todo: Todo, fieldsToUpdate: Partial) { 12 | return {...todo, ...fieldsToUpdate}; 13 | } 14 | 15 | const todo1 = { 16 | title: 'Organise Desk', 17 | description: 'Clear clutter' 18 | }; 19 | 20 | const todo2 = updateTodo(todo1, {title: 'Clean materials'}); -------------------------------------------------------------------------------- /02. types/11. literal-inference/main.ts: -------------------------------------------------------------------------------- 1 | const req = { url: "https://example.com", method: "GET" }; 2 | handleRequest(req.url, req.method); 3 | 4 | //Argument of type 'string' is not assignable to parameter of type '"GET" | "POST" 5 | 6 | 7 | function handleRequest(url: string, method: 'GET' | 'POST'): void {} 8 | 9 | 10 | //FIX 11 | 12 | // Change 1: 13 | const req2 = { url: "https://example.com", method: "GET" as "GET" }; 14 | // Change 2 15 | handleRequest(req2.url, req2.method as "GET"); 16 | 17 | 18 | const req3 = { url: "https://example.com", method: "GET" } as const; 19 | handleRequest(req3.url, req3.method); -------------------------------------------------------------------------------- /07. classes/02. getters-and-setters/main.ts: -------------------------------------------------------------------------------- 1 | class C { 2 | _length = 0; 3 | get length() { 4 | return this._length; 5 | } 6 | set length(value) { 7 | this._length = value; 8 | } 9 | } 10 | 11 | class Thing { 12 | _size = 0; 13 | 14 | get size(): number { 15 | return this._size; 16 | } 17 | 18 | set size(value: string | number | boolean) { 19 | let num = Number(value); 20 | 21 | // Don't allow NaN, Infinity, etc 22 | 23 | if (!Number.isFinite(num)) { 24 | this._size = 0; 25 | return; 26 | } 27 | 28 | this._size = num; 29 | } 30 | } -------------------------------------------------------------------------------- /03. narrowing/08. type-predicate-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | function isFish(pet: Fish | Bird): pet is Fish { 2 | return (pet as Fish).swim !== undefined; 3 | } 4 | 5 | const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()]; 6 | const underWater1: Fish[] = zoo.filter(isFish); 7 | // or, equivalently 8 | const underWater2: Fish[] = zoo.filter(isFish) as Fish[]; 9 | 10 | // The predicate may need repeating for more complex examples 11 | const underWater3: Fish[] = zoo.filter((pet): pet is Fish => { 12 | if (pet.name === "sharkey") return false; 13 | return isFish(pet); 14 | }); 15 | 16 | 17 | function getSmallPet(): Fish | Bird { 18 | return null; 19 | } -------------------------------------------------------------------------------- /06. type-manipulation/05. distributive-conditional-types/main.ts: -------------------------------------------------------------------------------- 1 | 2 | type ToArray = Type extends any ? Type[] : never; 3 | 4 | // If we plug a union type into ToArray, then the conditional type will be applied to each member of that union. 5 | 6 | 7 | type ToArray = Type extends any ? Type[]: never; 8 | type StrArrOrNumber = ToArray; 9 | 10 | // Typically, distributivity is the desired behavior. To avoid that behavior, you can surround each side of the extends keyword with square brackets. 11 | type ToArrayNonDist = [Type] extends [any] ? Type[] : never; 12 | 13 | // 'StrOrNumArr' is no longer a union. 14 | type StrOrNumArr = ToArrayNonDist; -------------------------------------------------------------------------------- /03. narrowing/04. in-operator-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | type Fish = { swim: () => void }; 2 | type Bird = { fly: () => void }; 3 | 4 | function move(animal: Fish | Bird) { 5 | if ("swim" in animal) { 6 | return animal.swim(); 7 | } 8 | 9 | return animal.fly(); 10 | } 11 | 12 | 13 | // Optional parameters 14 | 15 | type Fish = { swim: () => void }; 16 | type Bird = { fly: () => void }; 17 | type Human = { swim?: () => void, fly?: () => void }; 18 | 19 | function move(animal: Fish | Bird | Human) { 20 | if ("swim" in animal) { 21 | animal 22 | 23 | // (parameter) animal: Fish | Human 24 | } else { 25 | animal 26 | 27 | // (parameter) animal: Bird | Human 28 | } 29 | } -------------------------------------------------------------------------------- /03. narrowing/02. truthiness-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | // both of these result in 'true' 2 | Boolean("hello"); // type: boolean, value: true 3 | !!"world"; // type: true, value: true 4 | 5 | 6 | 7 | function printAll(strs: string | string[] | null) { 8 | if (strs && typeof strs === "object") { 9 | for (const s of strs) { 10 | console.log(s); 11 | } 12 | } else if (typeof strs === "string") { 13 | console.log(strs); 14 | } 15 | } 16 | 17 | 18 | function multiplyAll(values: number[] | undefined, factor: number): number[] | undefined { 19 | if (!values) { 20 | return values; 21 | } else { 22 | return values.map((x) => x * factor); 23 | } 24 | } -------------------------------------------------------------------------------- /06. type-manipulation/02. typeof-operator/main.ts: -------------------------------------------------------------------------------- 1 | let s = "hello"; 2 | let n: typeof s; 3 | 4 | // let n: string 5 | 6 | 7 | 8 | type Predicate = (x: unknown) => boolean; 9 | type K = ReturnType; 10 | // type K = boolean 11 | 12 | 13 | function f() { 14 | return { x: 10, y: 3 }; 15 | } 16 | type P = ReturnType; 17 | 18 | // 'f' refers to a value, but is being used as a type here. Did you mean 'typeof f'? 19 | 20 | type P = ReturnType; 21 | 22 | // type P = { 23 | // x: number; 24 | // y: number; 25 | // } 26 | 27 | 28 | 29 | // LIMITATIONS 30 | 31 | // Meant to use = ReturnType 32 | let shouldContinue: typeof msgbox("Are you sure you want to continue?"); -------------------------------------------------------------------------------- /06. type-manipulation/09. template-literal-types/main.ts: -------------------------------------------------------------------------------- 1 | type EmailLocaleIDs = "welcome_email" | "email_heading"; 2 | type FooterLocaleIDs = "footer_title" | "footer_sendoff"; 3 | 4 | type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`; 5 | 6 | 7 | type Lang = "en" | "ja" | "pt"; 8 | 9 | type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`; 10 | 11 | 12 | type PropEventSource = { 13 | on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void; 14 | }; 15 | 16 | /// Create a "watched object" with an 'on' method 17 | /// so that you can watch for changes to properties. 18 | declare function makeWatchedObject(obj: Type): Type & PropEventSource; 19 | 20 | 21 | -------------------------------------------------------------------------------- /06. type-manipulation/07. mapping-modifiers/main.ts: -------------------------------------------------------------------------------- 1 | // Removes 'readonly' attributes from a type's properties 2 | type CreateMutable = { 3 | -readonly [Property in keyof Type]: Type[Property]; 4 | }; 5 | 6 | type LockedAccount = { 7 | readonly id: string; 8 | readonly name: string; 9 | }; 10 | 11 | type UnlockedAccount = CreateMutable; 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | // Removes 'optional' attributes from a type's properties 21 | type Concrete = { 22 | [Property in keyof Type]-?: Type[Property]; 23 | }; 24 | 25 | type MaybeUser = { 26 | id: string; 27 | name?: string; 28 | age?: number; 29 | }; 30 | 31 | type User = Concrete; -------------------------------------------------------------------------------- /04. functions/13. unknown-function-types/main.ts: -------------------------------------------------------------------------------- 1 | // The unknown type represents any value. 2 | // This is similar to the any type, but is safer because it’s not legal to do anything with an unknown value: 3 | 4 | 5 | function f1(a: any) { 6 | a.b(); // OK 7 | } 8 | function f2(a: unknown) { 9 | a.b(); 10 | // Object is of type 'unknown'. 11 | } 12 | 13 | 14 | // This is useful when describing function types because you can describe functions 15 | // that accept any value without having any values in your function body. 16 | // Conversely, you can describe a function that returns a value of unknown type: 17 | 18 | function safeParse(s: string): unknown { 19 | return JSON.parse(s); 20 | } 21 | 22 | const obj = safeParse('sdfjkdfa'); -------------------------------------------------------------------------------- /09. utility-types/06. omit-type/main.ts: -------------------------------------------------------------------------------- 1 | // Omit 2 | // Constructs a type by picking all properties from Type and then 3 | // removing Keys (string literal or union of string literals). 4 | 5 | 6 | interface Todo { 7 | title: string; 8 | description: string; 9 | completed: boolean; 10 | createdAt: number; 11 | } 12 | 13 | type TodoPreview = Omit; 14 | 15 | const todo: TodoPreview = { 16 | title: "Clean room", 17 | completed: false, 18 | createdAt: 1615544252770, 19 | }; 20 | 21 | 22 | type TodoInfo = Omit; 23 | 24 | const todoInfo: TodoInfo = { 25 | title: "Pick up kids", 26 | description: "Kindergarten closes at 5pm", 27 | }; 28 | -------------------------------------------------------------------------------- /06. type-manipulation/10. inference-with-literals/main.ts: -------------------------------------------------------------------------------- 1 | type PropEventSource = { 2 | on 3 | (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void ): void; 4 | }; 5 | 6 | declare function makeWatchedObject(obj: Type): Type & PropEventSource; 7 | 8 | const person = makeWatchedObject({ 9 | firstName: "Saoirse", 10 | lastName: "Ronan", 11 | age: 26 12 | }); 13 | 14 | person.on("firstNameChanged", newName => { 15 | // (parameter) newName: string 16 | console.log(`new name is ${newName.toUpperCase()}`); 17 | }); 18 | 19 | person.on("ageChanged", newAge => { 20 | // (parameter) newAge: number 21 | if (newAge < 0) { 22 | console.warn("warning! negative age"); 23 | } 24 | }) -------------------------------------------------------------------------------- /09. utility-types/13. instance-type/main.ts: -------------------------------------------------------------------------------- 1 | // InstanceType 2 | // Constructs a type consisting of the instance type of a constructor function in Type. 3 | 4 | class C { 5 | x = 0; 6 | y = 0; 7 | } 8 | 9 | type T0 = InstanceType; 10 | // type T0 = C 11 | 12 | type T1 = InstanceType; 13 | // type T1 = any 14 | 15 | type T2 = InstanceType; 16 | // type T2 = never 17 | 18 | type T3 = InstanceType; 19 | // Type 'string' does not satisfy the constraint 'abstract new (...args: any) => any'. 20 | // type T3 = any 21 | 22 | 23 | type T4 = InstanceType; 24 | // Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'. 25 | // Type 'Function' provides no match for the signature 'new (...args: any): any'. 26 | // type T4 = any -------------------------------------------------------------------------------- /05. object-types/03. index-signatures/main.ts: -------------------------------------------------------------------------------- 1 | // This index signature states that when a StringArray is indexed with a number, 2 | // it will return a string. 3 | 4 | 5 | interface StringArray { 6 | [index: number]: string; 7 | } 8 | 9 | const myArray: StringArray = getStringArray(); 10 | const secondItem = myArray[1]; 11 | 12 | function getStringArray(): StringArray { 13 | throw new Error("Function not implemented."); 14 | } 15 | 16 | 17 | interface NumberDictionary { 18 | [index: string]: number; 19 | 20 | length: number; // ok 21 | name: string; 22 | // Property 'name' of type 'string' is not assignable to 'string' index type 'number'. 23 | } 24 | 25 | 26 | interface NumberOrStringDictionary { 27 | [index: string]: number | string; 28 | length: number; // ok, length is a number 29 | name: string; // ok, name is a string 30 | } -------------------------------------------------------------------------------- /09. utility-types/11. contructor-parameter-type/main.ts: -------------------------------------------------------------------------------- 1 | // ConstructorParameters 2 | // Constructs a tuple or array type from the types of a constructor function type. 3 | // It produces a tuple type with all the parameter types (or the type never if Type is not a function). 4 | 5 | type T0 = ConstructorParameters; 6 | // type T0 = [message?: string] 7 | 8 | type T1 = ConstructorParameters; 9 | // type T1 = string[] 10 | 11 | type T2 = ConstructorParameters; 12 | // type T2 = [pattern: string | RegExp, flags?: string] 13 | 14 | type T3 = ConstructorParameters; 15 | // type T3 = unknown[] 16 | 17 | type T4 = ConstructorParameters; 18 | // Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'. 19 | // Type 'Function' provides no match for the signature 'new (...args: any): any'. 20 | // type T4 = never -------------------------------------------------------------------------------- /03. narrowing/10. exhaustiveness-checking-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | type Shape = Circle | Square; 2 | 3 | function getArea(shape: Shape) { 4 | switch (shape.kind) { 5 | case "circle": 6 | return Math.PI * shape.radius ** 2; 7 | case "square": 8 | return shape.sideLength ** 2; 9 | default: 10 | const _exhaustiveCheck: never = shape; 11 | return _exhaustiveCheck; 12 | } 13 | } 14 | 15 | 16 | 17 | interface Triangle { 18 | kind: "triangle"; 19 | sideLength: number; 20 | } 21 | 22 | type Shape = Circle | Square | Triangle; 23 | 24 | function getArea(shape: Shape) { 25 | switch (shape.kind) { 26 | case "circle": 27 | return Math.PI * shape.radius ** 2; 28 | case "square": 29 | return shape.sideLength ** 2; 30 | default: 31 | const _exhaustiveCheck: never = shape; 32 | // Type 'Triangle' is not assignable to type 'never'. 33 | return _exhaustiveCheck; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /05. object-types/09. ReadOnlyArray-type/main.ts: -------------------------------------------------------------------------------- 1 | function doStuff(values: ReadonlyArray) { 2 | // We can read from 'values'... 3 | const copy = values.slice(); 4 | console.log(`The first value is ${values[0]}`); 5 | 6 | // ...but we can't mutate 'values'. 7 | values.push("hello!"); 8 | // Property 'push' does not exist on type 'readonly string[]'. 9 | } 10 | 11 | 12 | 13 | // Unlike Array, there isn’t a ReadonlyArray constructor that we can use. 14 | 15 | new ReadonlyArray("red", "green", "blue"); 16 | // 'ReadonlyArray' only refers to a type, but is being used as a value here. 17 | 18 | 19 | // Instead, we can assign regular Arrays to ReadonlyArrays. 20 | 21 | const roArray: ReadonlyArray = ["red", "green", "blue"]; 22 | 23 | 24 | 25 | // Just as TypeScript provides a shorthand syntax for Array with Type[], 26 | // it also provides a shorthand syntax for ReadonlyArray with readonly Type[]. -------------------------------------------------------------------------------- /01. basics/09. definite-assignment-assertion-operator/main.ts: -------------------------------------------------------------------------------- 1 | class C { 2 | foo: number; // OKAY as assigned in constructor 3 | bar: string = "hello"; // OKAY as has property initializer 4 | baz: boolean; // TS ERROR: Property 'baz' has no initializer and is not assigned directly in the constructor. 5 | constructor() { 6 | this.foo = 42; 7 | } 8 | } 9 | 10 | 11 | class D { 12 | foo!: number; 13 | // ^ 14 | // Notice this exclamation point! 15 | // This is the "definite assignment assertion" modifier. 16 | 17 | constructor() { 18 | this.initialize(); 19 | } 20 | initialize() { 21 | this.foo = 0; 22 | } 23 | } 24 | 25 | 26 | let a: number[]; // No assertion 27 | let b!: number[]; // Assert 28 | 29 | initialize(); 30 | 31 | a.push(4); // TS ERROR: variable used before assignment 32 | b.push(4); // OKAY: because of the assertion 33 | 34 | function initialize() { 35 | a = [0, 1, 2, 3]; 36 | b = [0, 1, 2, 3]; 37 | } -------------------------------------------------------------------------------- /06. type-manipulation/06. mapped-types/main.ts: -------------------------------------------------------------------------------- 1 | // Mapped types build on the syntax for index signatures, 2 | // which are used to declare the types of properties 3 | // which has not been declared ahead of time: 4 | 5 | 6 | type Horse = {} 7 | 8 | type OnlyBoolsAndHorses = { 9 | [key: string]: boolean | Horse; 10 | } 11 | 12 | const conforms: OnlyBoolsAndHorses = { 13 | del: true, 14 | rodney: false, 15 | } 16 | 17 | 18 | // A mapped type is a generic type which uses a union of PropertyKeys 19 | // (frequently created via a keyof) to iterate through keys to create a type: 20 | 21 | 22 | type OptionsFlag = { 23 | [Property in keyof Type]: boolean; 24 | } 25 | 26 | // In this example, OptionsFlags will take all the properties from the 27 | // type Type and change their values to be a boolean. 28 | 29 | type FeatureFlags = { 30 | darkMode: () => void; 31 | newUserProfile: () => void; 32 | } 33 | 34 | 35 | type FeatureOptions = OptionsFlag; 36 | -------------------------------------------------------------------------------- /03. narrowing/03. equality-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | function example(x: string | number, y: string | boolean) { 2 | if (x === y) { 3 | // We can now call any 'string' method on 'x' or 'y'. 4 | x.toUpperCase(); 5 | 6 | // (method) String.toUpperCase(): string 7 | y.toLowerCase(); 8 | 9 | // (method) String.toLowerCase(): string 10 | } else { 11 | console.log(x); 12 | 13 | // (parameter) x: string | number 14 | console.log(y); 15 | 16 | // (parameter) y: string | boolean 17 | } 18 | } 19 | 20 | 21 | interface Container { 22 | value: number | null | undefined; 23 | } 24 | 25 | function multiplyValue(container: Container, factor: number) { 26 | // Remove both 'null' and 'undefined' from the type. 27 | if (container.value != null) { 28 | console.log(container.value); 29 | 30 | // (property) Container.value: number 31 | 32 | // Now we can safely multiply 'container.value'. 33 | container.value *= factor; 34 | } 35 | } -------------------------------------------------------------------------------- /09. utility-types/10. parameter-type/main.ts: -------------------------------------------------------------------------------- 1 | // Parameters 2 | // Constructs a tuple type from the types used in the parameters of a function type Type 3 | 4 | 5 | declare function f1(arg: {a: number, b:string}): void; 6 | 7 | 8 | type T0 = Parameters<() => string>; 9 | // type T0 = [] 10 | 11 | type T1 = Parameters<(s: string) => void>; 12 | // type T1 = [s: string] 13 | 14 | type T2 = Parameters<(arg: T) => T>; 15 | // type T2 = [arg: unknown] 16 | 17 | type T3 = Parameters; 18 | // type T3 = [arg: { 19 | // a: number; 20 | // b: string; 21 | // }] 22 | 23 | type T4 = Parameters; 24 | // type T4 = unknown[] 25 | 26 | type T5 = Parameters; 27 | // type T5 = never 28 | 29 | type T6 = Parameters; 30 | // Type 'string' does not satisfy the constraint '(...args: any) => any'. 31 | // type T6 = never 32 | 33 | type T7 = Parameters; 34 | // Type 'Function' does not satisfy the constraint '(...args: any) => any'. 35 | // Type 'Function' provides no match for the signature '(...args: any): any'. 36 | 37 | // type T7 = never -------------------------------------------------------------------------------- /07. classes/10. this-based-type-guards/main.ts: -------------------------------------------------------------------------------- 1 | class FileSystemObject { 2 | isFile(): this is FileRep { 3 | return this instanceof FileRep; 4 | } 5 | isDirectory(): this is Directory { 6 | return this instanceof Directory; 7 | } 8 | isNetworked(): this is Networked & this { 9 | return this.networked; 10 | } 11 | constructor(public path: string, private networked: boolean) {} 12 | } 13 | 14 | class FileRep extends FileSystemObject { 15 | constructor(path: string, public content: string) { 16 | super(path, false); 17 | } 18 | } 19 | 20 | class Directory extends FileSystemObject { 21 | children: FileSystemObject[]; 22 | } 23 | 24 | interface Networked { 25 | host: string; 26 | } 27 | 28 | const fso: FileSystemObject = new FileRep("foo/bar.txt", "foo"); 29 | 30 | if (fso.isFile()) { 31 | fso.content; 32 | // const fso: FileRep 33 | } else if (fso.isDirectory()) { 34 | fso.children; 35 | // const fso: Directory 36 | } else if (fso.isNetworked()) { 37 | fso.host; 38 | // const fso: Networked & FileSystemObject 39 | } -------------------------------------------------------------------------------- /09. utility-types/12. return-type/main.ts: -------------------------------------------------------------------------------- 1 | // ReturnType 2 | // Constructs a type consisting of the return type of function Type. 3 | 4 | 5 | declare function f1(): { a: number; b: string }; 6 | 7 | type T0 = ReturnType<() => string>; 8 | // type T0 = string 9 | 10 | type T1 = ReturnType<(s: string) => void>; 11 | // type T1 = void 12 | 13 | type T2 = ReturnType<() => T>; 14 | // type T2 = unknown 15 | 16 | type T3 = ReturnType<() => T>; 17 | // type T3 = number[] 18 | 19 | type T4 = ReturnType; 20 | // type T4 = { 21 | // a: number; 22 | // b: string; 23 | // } 24 | 25 | type T5 = ReturnType; 26 | // type T5 = any 27 | 28 | type T6 = ReturnType; 29 | // type T6 = never 30 | 31 | type T7 = ReturnType; 32 | // Type 'string' does not satisfy the constraint '(...args: any) => any'. 33 | // type T7 = any 34 | 35 | type T8 = ReturnType; 36 | // Type 'Function' does not satisfy the constraint '(...args: any) => any'. 37 | // Type 'Function' provides no match for the signature '(...args: any): any'. 38 | // type T8 = any -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Divin Irakiza 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 | -------------------------------------------------------------------------------- /03. narrowing/01. type-guards-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | function padLeft(padding: number | string, input: string) { 2 | return new Array(padding + 1).join(" ") + input; 3 | // Operator '+' cannot be applied to types 'string | number' and 'number'. 4 | } 5 | 6 | 7 | function padLeft2(padding: number | string, input: string) { 8 | if (typeof padding === "number") { 9 | return new Array(padding + 1).join(" ") + input; 10 | } 11 | return padding + input; 12 | } 13 | 14 | 15 | function padLeft3(padding: number | string, input: string) { 16 | if (typeof padding === "number") { 17 | return new Array(padding + 1).join(" ") + input; 18 | 19 | // (parameter) padding: number 20 | } 21 | return padding + input; 22 | 23 | // (parameter) padding: string 24 | } 25 | 26 | 27 | 28 | function printAll(strs: string | string[] | null) { 29 | if (typeof strs === "object") { 30 | for (const s of strs) { 31 | // Object is possibly 'null'. 32 | console.log(s); 33 | } 34 | } else if (typeof strs === "string") { 35 | console.log(strs); 36 | } else { 37 | // do nothing 38 | } 39 | } -------------------------------------------------------------------------------- /06. type-manipulation/03. indexed-access-types/main.ts: -------------------------------------------------------------------------------- 1 | type Person = {age: number, name: string, alive: boolean}; 2 | 3 | type Age = Person["age"]; 4 | // type Age = number; 5 | 6 | 7 | type T1 = Person["age" | "name"]; 8 | // type T1 = string | number; 9 | 10 | 11 | type T2 = Person[keyof Person]; 12 | // type T2 = string | number | boolean 13 | 14 | 15 | type AliveOrName = "alive" | "name"; 16 | type T3 = Person[AliveOrName]; 17 | // type td3 = string | boolean 18 | 19 | 20 | 21 | 22 | // Advanced example 23 | 24 | const MyArray = [ 25 | { name: "Alice", age: 15 }, 26 | { name: "Bob", age: 23 }, 27 | { name: "Eve", age: 38 }, 28 | ]; 29 | 30 | type Person = typeof MyArray[number]; 31 | // type Person = { 32 | // name: string; 33 | // age: number; 34 | // } 35 | 36 | type Age = typeof MyArray[number]["age"]; 37 | // type Age = number 38 | 39 | // Or 40 | type Age2 = Person["age"]; 41 | // type Age2 = number 42 | 43 | 44 | 45 | const key = "age"; 46 | type Age = Person[key]; 47 | // Type 'any' cannot be used as an index type. 48 | // 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'? 49 | 50 | 51 | type key = "age"; 52 | type Age = Person[key]; -------------------------------------------------------------------------------- /05. object-types/07. generic-object-types/main.ts: -------------------------------------------------------------------------------- 1 | interface Box { 2 | contents: any; 3 | } 4 | 5 | 6 | interface Box { 7 | contents: unknown; 8 | } 9 | 10 | let x: Box = { 11 | contents: "hello world", 12 | }; 13 | 14 | // we could check 'x.contents' 15 | if (typeof x.contents === "string") { 16 | console.log(x.contents.toLowerCase()); 17 | } 18 | 19 | // or we could use a type assertion 20 | console.log((x.contents as string).toLowerCase()) 21 | 22 | 23 | 24 | interface NumberBox { 25 | contents: number; 26 | } 27 | 28 | interface StringBox { 29 | contents: string; 30 | } 31 | 32 | interface BooleanBox { 33 | contents: boolean; 34 | } 35 | 36 | 37 | interface Box { 38 | contents: Type; 39 | } 40 | 41 | 42 | let boxA: Box = { contents: "hello" }; 43 | boxA.contents; 44 | 45 | 46 | 47 | 48 | type OrNull = Type | null; 49 | 50 | type OneOrMany = Type | Type[]; 51 | 52 | type OneOrManyOrNull = OrNull>; 53 | 54 | type OneOrManyOrNull = OneOrMany | null 55 | 56 | type OneOrManyOrNullStrings = OneOrManyOrNull; 57 | 58 | type OneOrManyOrNullStrings = OneOrMany | null -------------------------------------------------------------------------------- /06. type-manipulation/08. key-remapping/main.ts: -------------------------------------------------------------------------------- 1 | type NewKeyType = {}; 2 | 3 | type MappedTypeWithNewProperties = { 4 | [Properties in keyof Type as NewKeyType]: Type[Properties] 5 | } 6 | 7 | 8 | 9 | type Getters = { 10 | [Property in keyof Type as `get${Capitalize }`]: () => Type[Property]; 11 | } 12 | 13 | type Setters = { 14 | [Property in keyof Type as `set${Capitalize }`]: () => void; 15 | } 16 | 17 | 18 | interface Person { 19 | name: string; 20 | age: number; 21 | location: string; 22 | } 23 | 24 | 25 | type LazyPerson = Getters; 26 | type FancyPerson = Setters; 27 | 28 | 29 | 30 | // Remove the 'kind' property 31 | type RemoveKindField = { 32 | [Property in keyof Type as Exclude]: Type[Property] 33 | }; 34 | 35 | interface Circle { 36 | kind: "circle"; 37 | radius: number; 38 | } 39 | 40 | type KindlessCircle = RemoveKindField; 41 | 42 | 43 | type ExtractPII = { 44 | [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false; 45 | }; 46 | 47 | type DBFields = { 48 | id: { format: "incrementing" }; 49 | name: { type: string; pii: true }; 50 | }; 51 | 52 | type ObjectsNeedingGDPRDeletion = ExtractPII; -------------------------------------------------------------------------------- /04. functions/07. constraints/main.ts: -------------------------------------------------------------------------------- 1 | function longest(a: Type, b: Type) { 2 | if (a.length >= b.length) { 3 | return a; 4 | } else { 5 | return b; 6 | } 7 | } 8 | 9 | // longerArray is of type 'number[]' 10 | const longerArray = longest([1, 2], [1, 2, 3]); 11 | 12 | // longerString is of type 'string' 13 | const longerString = longest("alice", "bob"); 14 | 15 | // Error! Numbers don't have a 'length' property 16 | const notOK = longest(10, 100); 17 | // Argument of type 'number' is not assignable to parameter of type '{ length: number; }'. 18 | 19 | 20 | 21 | 22 | function minimumLength( 23 | obj: Type, 24 | minimum: number 25 | ): Type { 26 | if (obj.length >= minimum) { 27 | return obj; 28 | } else { 29 | return { length: minimum }; 30 | // Type '{ length: number; }' is not assignable to type 'Type'. 31 | // '{ length: number; }' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'. 32 | } 33 | } 34 | 35 | // 'arr' gets value { length: 6 } 36 | const arr = minimumLength([1, 2, 3], 6); 37 | // and crashes here because arrays have 38 | // a 'slice' method, but not the returned object! 39 | console.log(arr.slice(0)); -------------------------------------------------------------------------------- /05. object-types/02. readonly-properties/main.ts: -------------------------------------------------------------------------------- 1 | // a property marked as readonly can’t be written to during type-checking. 2 | 3 | interface SomeType { 4 | readonly prop: string; 5 | } 6 | 7 | function doSomething(obj: SomeType) { 8 | // We can read from 'obj.prop'. 9 | console.log(`prop has the value '${obj.prop}'.`); 10 | 11 | // But we can't re-assign it. 12 | obj.prop = "hello"; 13 | // Cannot assign to 'prop' because it is a read-only property. 14 | } 15 | 16 | 17 | 18 | 19 | interface Home { 20 | readonly resident: { name: string; age: number }; 21 | } 22 | 23 | function visitForBirthday(home: Home) { 24 | // We can read and update properties from 'home.resident'. 25 | console.log(`Happy birthday ${home.resident.name}!`); 26 | home.resident.age++; 27 | } 28 | 29 | 30 | 31 | // the readonly modifier doesn’t necessarily imply that a value is totally immutable 32 | // - or in other words, that its internal contents can’t be changed. 33 | // It just means the property itself can’t be re-written to. 34 | 35 | function evict(home: Home) { 36 | // But we can't write to the 'resident' property itself on a 'Home'. 37 | home.resident = { 38 | // Cannot assign to 'resident' because it is a read-only property. 39 | name: "Victor the Evictor", 40 | age: 42, 41 | }; 42 | } -------------------------------------------------------------------------------- /04. functions/10. function-overloads/main.ts: -------------------------------------------------------------------------------- 1 | function makeDate(timestamp: number): Date; 2 | 3 | function makeDate(m: number, d: number, y: number): Date; 4 | 5 | function makeDate(mOrTimestamp: number, d?: number, y?: number): Date { 6 | if (d !== undefined && y !== undefined) { 7 | return new Date(y, mOrTimestamp, d); 8 | } else { 9 | return new Date(mOrTimestamp); 10 | } 11 | } 12 | 13 | const d1 = makeDate(12345678); 14 | 15 | const d2 = makeDate(5, 5, 5); 16 | 17 | const d3 = makeDate(1, 3); 18 | // No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments. 19 | 20 | 21 | function fn(x: string): void; 22 | function fn() { 23 | // ... 24 | } 25 | // Expected to be able to call with zero arguments 26 | fn(); 27 | 28 | 29 | 30 | 31 | function fn(x: boolean): void; 32 | // Argument type isn't right 33 | function fn(x: string): void; 34 | // This overload signature is not compatible with its implementation signature. 35 | function fn(x: boolean) {} 36 | 37 | 38 | 39 | 40 | // Let’s consider a function that returns the length of a string or an array: 41 | 42 | function len(s: string): number; 43 | function len(arr: any[]): number; 44 | function len(x: any) { 45 | return x.length; 46 | } 47 | 48 | 49 | 50 | // A better approach 51 | function len(x: any[] | string) { 52 | return x.length; 53 | } -------------------------------------------------------------------------------- /05. object-types/10. tuple-types/main.ts: -------------------------------------------------------------------------------- 1 | // StringNumberPair is a tuple type of string and number. 2 | // Like ReadonlyArray, it has no representation at runtime, 3 | // but is significant to TypeScript. 4 | 5 | type StringNumberPair = [string, number]; 6 | 7 | 8 | function doSomething(pair: [string, number]) { 9 | const a = pair[0]; 10 | 11 | const a: string 12 | const b = pair[1]; 13 | 14 | const b: number 15 | // ... 16 | } 17 | 18 | doSomething(["hello", 42]); 19 | 20 | type Either2dOr3d = [number, number, number?]; 21 | 22 | function setCoordinate(coord: Either2dOr3d) { 23 | const [x, y, z] = coord; 24 | 25 | const z: number | undefined 26 | 27 | console.log(`Provided coordinates had ${coord.length} dimensions`); 28 | 29 | // (property) length: 2 | 3 30 | } 31 | 32 | type StringNumberBooleans = [string, number, ...boolean[]]; 33 | type StringBooleansNumber = [string, ...boolean[], number]; 34 | type BooleansStringNumber = [...boolean[], string, number]; 35 | 36 | 37 | 38 | function readButtonInput(...args: [string, number, ...boolean[]]) { 39 | const [name, version, ...input] = args; 40 | // ... 41 | } 42 | 43 | // is basically equivalent to: 44 | 45 | function readButtonInput(name: string, version: number, ...input: boolean[]) { 46 | // ... 47 | } -------------------------------------------------------------------------------- /05. object-types/01.optional-properties/main.ts: -------------------------------------------------------------------------------- 1 | // In this example, both xPos and yPos are considered optional. 2 | // We can choose to provide either of them, so every call above to paintShape is valid. 3 | // All optionality really says is that if the property is set, 4 | // it better have a specific type. 5 | 6 | 7 | interface Shape {} 8 | 9 | interface PaintOptions { 10 | shape: Shape; 11 | xPos?: number; 12 | yPos?: number; 13 | } 14 | 15 | function paintShape(opts: PaintOptions) { 16 | // ... 17 | } 18 | 19 | const shape = getShape(); 20 | paintShape({ shape }); 21 | paintShape({ shape, xPos: 100 }); 22 | paintShape({ shape, yPos: 100 }); 23 | paintShape({ shape, xPos: 100, yPos: 100 }); 24 | 25 | 26 | 27 | function getShape(): Shape { 28 | throw new Error('Method unimplemented!') 29 | } 30 | 31 | 32 | 33 | 34 | . 35 | 36 | function paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) { 37 | console.log("x coordinate at", xPos); 38 | 39 | var xPos: number 40 | console.log("y coordinate at", yPos); 41 | 42 | var yPos: number 43 | // ... 44 | } 45 | 46 | // Here we used a destructuring pattern for paintShape’s parameter, 47 | // and provided default values for xPos and yPos. Now xPos and yPos are both definitely present within the body of paintShape, but optional for any callers to paintShape. -------------------------------------------------------------------------------- /03. narrowing/09. discriminated-unions-narrowing/main.ts: -------------------------------------------------------------------------------- 1 | interface Shape { 2 | kind: "circle" | "square"; 3 | radius?: number; 4 | sideLength?: number; 5 | } 6 | function handleShape(shape: Shape) { 7 | // oops! 8 | if (shape.kind === "rect") { 9 | // This condition will always return 'false' since the types '"circle" | "square"' and '"rect"' have no overlap. 10 | // ... 11 | } 12 | } 13 | 14 | 15 | 16 | function getArea(shape: Shape) { 17 | return Math.PI * shape.radius ** 2; 18 | // Object is possibly 'undefined'. 19 | } 20 | 21 | function getArea(shape: Shape) { 22 | if (shape.kind === "circle") { 23 | return Math.PI * shape.radius ** 2; 24 | // Object is possibly 'undefined'. 25 | } 26 | } 27 | 28 | function getArea(shape: Shape) { 29 | if (shape.kind === "circle") { 30 | return Math.PI * shape.radius! ** 2; 31 | } 32 | } 33 | 34 | 35 | // A clean approach 36 | 37 | interface Circle { 38 | kind: "circle"; 39 | radius: number; 40 | } 41 | 42 | interface Square { 43 | kind: "square"; 44 | sideLength: number; 45 | } 46 | 47 | type Shape = Circle | Square; 48 | 49 | 50 | function getArea(shape: Shape) { 51 | switch (shape.kind) { 52 | case "circle": 53 | return Math.PI * shape.radius ** 2; 54 | 55 | // (parameter) shape: Circle 56 | case "square": 57 | return shape.sideLength ** 2; 58 | 59 | // (parameter) shape: Square 60 | } 61 | } -------------------------------------------------------------------------------- /06. type-manipulation/04. conditional-types/main.ts: -------------------------------------------------------------------------------- 1 | interface Animal { 2 | live(): void; 3 | } 4 | interface Dog extends Animal { 5 | woof(): void; 6 | } 7 | 8 | type Example1 = Dog extends Animal ? number : string; 9 | // type Example1 = number 10 | 11 | type Example2 = RegExp extends Animal ? number : string; 12 | // type Example2 = string 13 | 14 | 15 | 16 | interface IdLabel { 17 | id: number /* some fields */; 18 | } 19 | interface NameLabel { 20 | name: string /* other fields */; 21 | } 22 | 23 | function createLabel(id: number): IdLabel; 24 | function createLabel(name: string): NameLabel; 25 | function createLabel(nameOrId: string | number): IdLabel | NameLabel; 26 | function createLabel(nameOrId: string | number): IdLabel | NameLabel { 27 | throw "unimplemented"; 28 | } 29 | 30 | // we can encode that logic in a conditional type: 31 | 32 | type NameOrId = T extends number 33 | ? IdLabel 34 | : NameLabel; 35 | 36 | 37 | function createLabel(idOrName: T): NameOrId { 38 | throw "unimplemented"; 39 | } 40 | 41 | let a = createLabel("typescript"); 42 | // let a: NameLabel 43 | 44 | let b = createLabel(2.8); 45 | // let b: IdLabel 46 | 47 | let c = createLabel(Math.random() ? "hello" : 42); 48 | // let c: NameLabel | IdLabel 49 | 50 | 51 | 52 | // we could also write a type called Flatten that flattens array types 53 | // to their element types, but leaves them alone otherwise: 54 | 55 | type Flatten = T extends any[] ? T[number] : T; 56 | 57 | // Extracts out the element type. 58 | type Str = Flatten; 59 | // type Str = string 60 | 61 | // Leaves the type alone. 62 | type Num = Flatten; 63 | // type Num = number 64 | 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | --------------------------------------------------------------------------------