Skip to content

clarklindev/typescript-stephengrider-basics-of-typescript

Repository files navigation

Typescript - StephenGrider - Basics of Typescript

NOTE: Section 01 to Section 09 - Basics of Typescript


Table of contents

Section 01 to Section 09 - basics of typescript

  1. How to Get Help (1min)
  2. Join Our Community! (1min)
  3. Course Resources (1min)
  4. Typescript Overview (6min)
  5. Environment Setup (8min)
  6. Important Axios and TypeScript Version Information (1min)
  7. A First App (5min)
  8. Executing Typescript Code (5min)
  9. One Quick Change (4min)
  10. Catching Errors with Typescript (7min)
  11. Catching More Errors! (5min)
  1. Do Not Skip - Course Overview (4min)
  2. Types (5min)
  3. More on Types (6min)
  4. Examples of Types (5min)
  5. Where Do We Use Types? (1min)
  1. Type Annotations and Inference (2min)
  2. Annotations with Variables (5min)
  3. Object Literal Annotations (7min)
  4. Annotations Around Functions (6min)
  5. Understanding Inference (4min)
  6. The 'Any' Type (8min)
  7. Fixing the 'Any' Type (2min)
  8. Delayed Initialization (3min)
  9. When Inference Doesn't Work (5min)
  1. More on Annotations Around Functions (5min)
  2. Inference Around Functions (6min)
  3. Annotations for Anonymous Functions (2min)
  4. Void and Never (3min)
  5. Destructuring with Annotations (4min)
  6. Annotations Around Objects (7min)
  1. Arrays in Typescript (5min)
  2. Why Typed Arrays? (5min)
  3. Multiple Types in Arrays (3min)
  4. When to Use Typed Arrays (1min)
  1. Tuples in Typescript (4min)
  2. Tuples in Action (5min)
  3. Why Tuples? (3min)
  1. Interfaces (1min)
  2. Long Type Annotations (5min)
  3. Fixing Long Annotations with Interfaces (5min)
  4. Syntax Around Interfaces (4min)
  5. Functions in Interfaces (5min)
  6. Code Reuse with Interfaces (4min)
  7. General Plan with Interfaces (3min)
  1. Classes (4min)
  2. Basic Inheritance (3min)
  3. Instance Method Modifiers (7min)
  4. Fields in Classes (6min)
  5. Fields with Inheritance (4min)
  6. Where to Use Classes (1min)
  1. Updated Parcel Instructions (1min)
  2. App Overview (3min)
  3. Bundling with Parcel (5min)
  4. Project Structure (3min)
  5. IMPORTANT Info About Faker Installation (1min)
  6. Generating Random Data (5min)
  7. Type Definition Files (5min)
  8. Using Type Definition Files (6min)
  9. Export Statements in Typescript (5min)
  10. Defining a Company (5min)
  11. Note on Generating an API Key (1min)
  12. Adding Google Maps Support (8min)
  13. Required Update for New @types Library (1min)
  14. Google Maps Integration (4min)
  15. Exploring Type Definition Files (3min)
  16. Hiding Functionality (6min)
  17. Why Use Private Modifiers? Here's Why (8min)
  18. Adding Markers (9min)
  19. Duplicate Code (3min)
  20. One Possible Solution (7min)
  21. Restricting Access with Interfaces (6min)
  22. Implicit Type Checks (3min)
  23. Showing Popup Windows (7min)
  24. Updating Interface Definitions (7min)
  25. Optional Implements Clauses (6min)
  26. App Wrapup (8min)

Section 01 - getting started with typescript

1. How to Get Help (1min)

  • @ste_grider
  • udemy Q&a

2. Join Our Community! (1min)

3. Course Resources (1min)

4. Typescript Overview (6min)

  • adding a type-system
  • catch errors during development
  • adding 'type annotations'
  • no performance optimizations

running the code

  • typescript (js + annotations) -> typescript compiler -> js

5. Environment Setup (8min)

  • install typescript compiler -> npm install -g typescript ts-node

    • ts-node -> allows compile AND run code using single command
    • tsc --help
    • vscode typescript add-on
    • install vs-code prettier
    • add .prettierrc
    {
    "tabWidth": 2,
    "useTabs": false,
    "printWidth": 80,
    "semi": true,
    "singleQuote": true,
    "trailingComma": "es5"
    }
    
    • enable format on save: CTRL + , -> search and enable 'Format On Save'

6. Important Axios and TypeScript Version Information (1min)

  • when using latest axios need types: npm install --save-dev @types/node
  • TS 5.6 has breaking changes (ts-node not nsync)
  • install typscript v5.5 npm install -g typescript@5.5
    • this fixes Cannot use import statement outside a module errors

7. A First App (5min)

A basic api fetching data

  • see project files: /tutorial-stephengrider-typescript-01-basic-example
  • TODO: make a network request to fetch some JSON and print the result
  • TODO: create a project folder
    • i've named mine: tutorial-stephengrider-typescript-01-basic-example
    • go into folder
  • TODO: create node project npm init -y
  • TODO: create index.js

installs

  • install axios
  • install ts-node
  • install typescript
npm i axios
npm i --save-dev ts-node typescript

fetch data using json typicode (jsonplaceholder.typicode.com)

8. Executing Typescript Code (5min)

# compile -> creates index.js
tsc index.ts
import axios from 'axios';
const url = 'https://jsonplaceholder.typicode.com/todos/1';

axios.get(url).then((response) => {
  console.log(response.data);
});
  • run the js
node index.js
  • expected output:
{ userId: 1, id: 1, title: 'delectus aut autem', completed: false }
// package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc index.ts",
    "dev": "node index.js",
    "start": "ts-node index.ts"
  },
  • ts-node (compile + run)
  • NOTE: running ts-node on its own does not create output .js file (like with tsc)

9. One Quick Change (4min)

  • makes some mistakes in code: passing wrong variable names
  • does not have type checking

10. Catching Errors with Typescript (7min)

  • below is the finished code (with Typescript)
  • added an interface: defining structure of an object
//index.ts
import axios from 'axios';

const url = "https://jsonplaceholder.typicode.com/todos/1";

//TYPESCRIPT: interface
interface Todo{
  id: number;
  title: string;
  completed: boolean;
}

axios.get(url).then(response => {
  const todo = response.data as Todo;  //TYPESCRIPT: as interface

  const id = todo.id;
  const title = todo.title;
  const completed = todo.completed;

  logTodo(id, title, completed);

});

//TYPESCRIPT: give type annotations to function parameters
const logTodo = (id:number, title: string, completed:boolean) => {
  console.log(`${id} ${title} ${completed}`)
}

11. Catching More Errors! (5min)

  • created the logTodo function
  • error was the order of arguments passed in
    • FIX: give the arguments types so it picks up if wrong prop passed-in

Section 02 - What is a type system

12. Do Not Skip - Course Overview (4min)

learning Typescript:

  • syntax + features
    • focus on this first, then design patterns
  • design patterns with TS (FOCUS OF COURSE)
    • eg. how to use interfaces to write re-usable code

course goals

  • Syntax + features - section 2 - understanding basic types in TS

  • Syntax + features - section 3 - Function typing + annotations

  • Syntax + features - Type definition files

  • Syntax + features - arrays in TS

  • Syntax + features - modules systems

  • Syntax + features - classes + refresher on OOP

  • design pattern - Projects

  • for each of the topics

    • definition
    • why?
    • examples
    • when to use

13. Types (5min)

  • Type -> easy way to refer to the different properties and functions that a value has

14. More on Types (6min)

  • 2 categories of types:
    • primitive types -> string, number, boolean, void, undefined, null, symbol
    • Object types -> functions, arrays, classes, objects
  • Types - used by typescript compiler to analyze code for errors
  • Types - allow other devs to understand what data values are in the codebase

15. Examples of Types (5min)

  • autocomplete once we assigned type as Date
const date = new Date();
  • Class
class Color {}
const red = new Color();

16. Where Do We Use Types? (1min)

  • where are types used -> everywhere

Section 03 - Type annotations in action

17. Type Annotations and Inference (2min)

  • type annotation -> code we add to let Typescript know what type of value a variable refers to

  • type inference -> typescript tries to figure out what type of value a variable refers to

  • type annotations AND type inference apply differently for:

    • variables
    • functions
    • objects

18. Annotations with Variables (5min)

  • annotation -> :type
const apples: number = 5; //:number is an annotation

19. Object Literal Annotations (7min)

array type annotation:

  • string[] an array of strings
  • number[] array of numbers
  • boolean[] array of booleans
//Array
let colors: string[] = ['red', 'green', 'blue']; //annotation: array of strings
let numbers: number[] = [1, 2, 3];
let booleans: bool[] = [true, false, false];

classes type annotations:

  • Car's type starts with capital (so its a class type)
//Class
class Car {}
let car: Car = new Car();

object literal type annotations:

  • when the annotation is an object
//Object
let point: { x: number; y: number } = {
  x: 20,
  y: 23,
};

20. Annotations Around Functions (6min)

  • when the annotation type is a function.
  • function annotation -> the function annotation define the props it receives, and the return type
//function
const logNumber: (i: number) => void = (i: number) => {
  console.log(i);
};

21. Understanding Inference (4min)

  • so if we remove the annotations, typescript infers the type -> meaning based on the value it determines the type.
  • when you make a variable (there is a left and right of equals sign)
    • variable declaration -> left of =
    • variable initialization -> right of =

automatic inference

  • automatic inference (typescript figures out whats the type) -> same line declaration and initialization
  • when do we use type inference?
    • always use type inference
const color = 'red'; //automatic inference because declaration (left) and initialization (right) are on same line

when do we add annotations?

  • 3 senarios for ADDING annotations:
    • when function returns 'any' type and we need to clarify the type
    • when we declare a variable on one line then initialize it later
    • when we want a variable to have a type that cant be inferred

22. senario: The 'Any' Type (8min)

  • when function returns any type
  • ie. response from axios call / JSON.parse() -> typescript cant figure out return type because JSON.parse() depends on input string value and can yield varying results/types.
const json = '{"x":10, "y":20}';
const coordinates = JSON.parse(json); //coordinates has type `any`
console.log(coordinates);
{x: 10, y:20}

23. Fixing the 'Any' Type (2min)

//...

const coordinates: { x: number; y: number } = JSON.parse(json); //FIX by adding type

//...

24. senario: Delayed Initialization (3min)

  • when we declare a variable on one line and initialize it later
  • when variable declaration and value assignment not on same line
let words = ['red', 'green', 'blue'];
let foundWords: boolean; //fix by adding type annotation

for (let i = 0; i < words.length; i++) {
  if (words[i] === 'green') {
    foundWord = true;
  }
}

25. senario: When Inference doesn't Work (5min)

  • eg. variable is reassigned a value with different type
let numbers = [-10, -1, 12];
let numbersAboveZero: false | number = false; //FIX: we give it a type of either: false OR number

for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] > 0) {
    numbersAboveZero = numbers[i];
  }
}

Section 04 - Annotations with functions and objects

26. More on Annotations Around Functions (5min)

  • before we were adding annotations for the variable declaration (left-side of equals sign)
  • this section deals with annotations/inference for the right side of equals sign (ie the function arguments and return value)
  • type annotations for function
    • code we add to tell typescript what type of arguments a function will receive and what type of values it will return
  • type inference for functions
    • typescript tries to figure out what type of value a function will return
    • there is no type inference for function arguments
    • type inference is only for return type of function BUT we will always annotate the return type
//here we annotate return type as :number
const add = (a: number, b: number): number => {
  return a + b;
};

27. Inference Around Functions (6min)

  • typescript ensures correct return value by inferring return value (but not correct logic inside function)
  • we always annotate return type (this is for arrow functions, named functions, anonymous functions assigned to variable type) because there are times when we can forget to return something...and typecript will infer return as 'void'
  • :void return type for return null or undefined

28. Annotations for Anonymous Functions (2min)

  • arrow function
  • anonymous function
  • assigned function
//arrow function
const subtract = (a: number, b: number): number => {
  return a - b;
};

//anonymous function
function divide(a: number, b: number): number {
  return a / b;
}

//assigned function
const multiply = function (a: number, b: number): number {
  return a * b;
};

29. Void and Never (3min)

  • :void -> there is no return

  • :never

    • when we ONLY throw errors and dont return something so there is no way function will complete
    • if the function eventually returns something but could throw error, its return type is still for a success completion case
//never
const throwError = (message: string): never => {
  throw new Error(message);
};

30. Destructuring with Annotations (4min)

BEFORE

const todaysWeather = {
  date: new Date(),
  weather: 'sunny',
};

const logWeather = (forecast: { date: Date; weather: string }): void => {
  console.log(forecast.date);
  console.log(forecast.weather);
};

logWeather(todaysWeather);
  • destructuring date, and weather out of forecast
  • you can do this by replacing forecast with the props you want to get out of forecast
    • NOTE: the annotation type and destructure are separate

AFTER

const todaysWeather = {
  date: new Date(),
  weather: 'sunny',
};

const logWeather = (
  //<destructure of `forecast`> : <annotation>
  {
    date,
    weather,
  }: {
    date: Date;
    weather: string;
  }
): void => {
  console.log(date);
  console.log(weather);
};

logWeather(todaysWeather);

31. Annotations Around Objects (7min)

const profile = {
  name: 'alex',
  age: 20,
  coords: {
    lat: 0,
    lng: 15,
  },
  setAge(age: number): void {
    this.age = age;
  },
};
  • normal object destructuring age out of profile
const { age } = profile;

annotation for destructured 'profile'

  • with annotations -> add : <structure of profile object>
  • to understand this: {age} is the destructured object prop, so to annotate, add: {the expected attribute of profile structure}
const { age }: { age: number } = profile; //with annotation for destructured 'profile'

annotation for destructured 'profile' prop 'coords' (which is also an object)

  • to understand this: {coords: { lat, lng }} is the destructured object prop
  • so to annotate, add: {the expected attribute of profile structure} eg. { coords: { lat: number; lng: number } }
  • note: {the expected attribute of profile structure} is an object to allow destructing multiple props out of 'profile'
const {
  coords: { lat, lng },
}: { coords: { lat: number; lng: number } } = profile; //with annotation for destructured 'profile'

Section 05 - Mastering Typed Arrays

32. Arrays in Typescript (5min)

  • typed arrays - arrays where each element has a consistent type
  • type inference looking at values stored
  • initializing empty arrays -> add annotation
const carMakers: string[] = ['ford', 'toyota', 'chevy']; //with annotation, but without the annotation there is auto type inference
  • 2d array (type annotation is optional)
const carsByMake:string[][] = [['ford'], ['mazda'], ['bmw']];

33. Why Typed Arrays? (5min)

  • type inference when pulling value out of array
  • adding incompatible values
  • Array has helper functions
  • flexible (arrays can contain multiple different types)

34. Multiple Types in Arrays (3min)

  • override type inference by adding annotation (eg. when initializing with empty array)
const importantDates: (Date | string)[] = [new Date()];

35. When to Use Typed Arrays (1min)

  • when to use? -> when you have a collection of records with some arbitary sort order

Section 06 - Tuples in Typescript

36. Tuples in Typescript (4min)

  • with tuple, the annotation describes the type of the properties and order of the values
  • difference -> array like structure, BUT.. each element represents some property of a record
  • array example: [brown , true, 40] there is loss of information because we dont know what the properties are associated with which values - so order is important and needs to be remembered

type alias

  • you can replace tuple structure by defining a type alias
type Drink = [string, boolean, number]; //type alias

37. Tuples in Action (5min)

  • eg. plain object
//eg. plain object
const drink = {
  color: 'brown',
  carbonated: true,
  sugar: 40,
};
  • eg. array
//array
const pepsi = ['brown', true, 40];
  • same same
    • const pepsi: [string, boolean, number];
    • const pepsi: Drink;
//type alias
type Drink = [string, boolean, number]; //type alias

//tuple
const pepsi: [string, boolean, number] = ['brown', true, 40];

//using type alias
const pepsi: Drink = ['brown', true, 40];
const sprite: Drink = ['clear', true, 40];
const tea: Drink = ['brown', false, 0];

38. Why Tuples? (3min)

  • NOTE: prefer using objects because object keys have meaning whereas tuples only define the value type and order
  • wont use it often... maybe with .csv file
  • wont use it because even with the type defined in a tuple (order of values in tuple, the type has no meaning),
const carSpecs: [number, number] = [400, 3354];
  • using an object instead because there are meaningful 'keys'
const carStats = {
  horsePower: 400,
  weight: 3345,
};

Section 07 - The all important interface

39. Interfaces (1min)

  • create a new type describing the property names and value types of an object
  • ie. creating a new type (a custom type just like 'string' or 'number')

40. Long Type Annotations (5min)

  • this is just an example of using a long type annotation (not using interface)
  • wont use this way, because everytime you have a function receiving oldCivic you have to type out the long annotation type
const oldCivic = {
  name: 'civic',
  year: 2000,
  broken: true,
};

const printVehicle = (vehicle: {
  name: string;
  year: number;
  broken: boolean;
}): void => {
  console.log(
    `name: ${vehicle.name}, year: ${vehicle.year}, broken: ${vehicle.broken}`
  );
};

41. Fixing Long Annotations with Interfaces (5min)

  • adding an interface
  • generic name for interface (capital case)
interface Vehicle {
  name: string;
  year: number;
  broken: boolean;
}

const oldCivic = {
  name: 'civic',
  year: 2000,
  broken: true,
};

const printVehicle = (vehicle: Vehicle): void => {
  console.log(
    `name: ${vehicle.name}, year: ${vehicle.year}, broken: ${vehicle.broken}`
  );
};

printVehicle(oldCivic);

42. Syntax Around Interfaces (4min)

  • interfaces can have properties of complex type eg. Date
  • interfaces can have functions:
//interface with function
interface Vehicle{
  //...
  year: Date; //complex type
  summary(): string {
    return `name is: ${this.name}`;
  };
}
  • full example
interface Vehicle {
  name: string;
  year: Date; //complex type
  broken: boolean;
  summary(): string; //function type
}

const oldCivic = {
  name: 'civic',
  year: new Date(),
  broken: true,
  summary(): string {
    return `name is: ${this.name}`;
  },
};

const printVehicle = (vehicle: Vehicle): void => {
  console.log(vehile.summary());
};

console.log(oldCivic.summary());

43. Functions in Interfaces (5min)

  • when typescript tries to decide if an object is of a type, the object can have more properties than the interface requires BUT not less
  • FIX -> with example below, we change interface Vehicle to Reportable because technically, printVehicle function only needs to receive a type with summary()
  • FIX -> the function PrintVehicle does not need to be called printVehicle anymore, it can just be generic printSummary
interface Reportable {
  summary(): string; //function type
}

const printSummary = (item: Reportable): void => {
  console.log(item.summary());
};

44. Code Reuse with Interfaces (4min)

  • we made the code more generic to be reusable
  • NOTE: civic and drink both have summary() function so they are both of type Reportable
    • meaning both objects can use printSummary
  • as long as these objects abide by an interface, we can create generic functions that receive objects that satisfy the interface type
  • the function can be used with any object that satisfies the Reportable interface
const drink = {
  color: 'brown',
  carbonated: true,
  sugar: 40
  summary():string{
    return `my drink has ${sugar} grams of sugar`
  }
}

const printSummary = (item: Reportable): void => {
  console.log(item.summary());
};

printSummary(oldCivic);
printSummary(drink);

45. General Plan with Interfaces (3min)

interfaces-general-plan-with-interaces

  • using interfaces for gatekeeping/compatible to functions is way we get reusability with typescript.

  • ie. create functions that are typed with interfaces

  • make objects satisfy the interface to use the generic function

  • make use of 'as TYPE' to type some data to an interface

  • Make interfaces for axios response.data objects

interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

axios.get(url).then((response) => {
  const todo = response.data as Todo; //TYPESCRIPT: as interface
});
  • give type annotations to function parameters ( parameters are what you can pass to a function and arguments are actually values passed in a function)

Section 08 - Building functionality with classes

46. Classes (4min)

  • class is a blueprint -> to create an object with some fields (values) and methods (functions) to represent an idea
  • How we will learn classes
  1. learn about class methods (function)

    • then you can call eg.vehicle.drive();
  2. learn about class fields (values)

class Vehicle {
  drive(): void {
    console.log('hello');
  }

  honk(): void {
    consol.log('honk');
  }
}

const vehicle = new Vehicle();
vehicle.drive();
vehicle.honk();

47. Basic Inheritance (3min)

  • you can extend a class
  • you can redefine methods in a class that extends another class that has a method with same name
class Vehicle {
  drive(): void {
    console.log('hello');
  }

  honk(): void {
    consol.log('honk');
  }
}

class Car extends Vehicle {
  drive(): void {
    console.log('vroom');
  }
}

const car = new Car();
//car has access to all 'Vehicle' methods
car.drive(); //prints 'vroom'

48. class method modifiers (7min)

  • keywords you can place on methods -> public (default), private, protected
  • goal is to restrict access to functions and variables (FOR OTHER DEVELOPERS)
    • public (DEFAULT) -> method can be called anywhere, anytime
    • private -> only be called by other methods in this class (NOT from the instance)
    • protected -> can be called by other methods in this class OR by other methods in child classes.
  • NOTE: on overriding modifier: if parent class has a modifier, you cannot change the modifier in child class.
class Vehicle {
  public drive(): void {
    console.log('hello');
  }

  public honk(): void {
    consol.log('honk');
  }
}

class Car extends Vehicle {
  private drive(): void {
    console.log('vroom');
  }

  startDriving(): void {
    this.drive();
  }
}

const car = new Car();
car.drive();
car.honk();
  • NOTE: if Car class has drive() method set-up as 'private', it cannot be accessed via the instance, only from internally within the class eg. startDriving()
class Car {
  private drive() {}

  startDriving() {
    this.drive();
  }
}

const car = new Car();
// car.drive();  //error
car.startDriving(); //ok
car.honk();

49. Fields in Classes (6min)

  • adding fields to classes
  • traditionally you have to initialize variables in classes with a value and if you want to pass a value in, you define a constructor
  • you either initialize variable on same line in class OR inside constructor BUT best is to just define the access modifier with the prop value/type and remove the variable declaration and initializing inside the constructor
  • simple syntax where instance variable is also created and assigned when value passed in:
  • passed in constructor prop value of 'private', cant access from outside the class (ie. cant access from instance )
  • passed in constructor prop value of 'protected', cant access from outside the class BUT can from within child classes
class Vehicle {
  // color: string;
  // constructor(color: string) {
  //   this.color = color;
  // }

  constructor(public color: string) {} //simple syntax where instance variable is also created and assigned when value passed in

  public drive(): void {
    console.log('hello');
  }

  public honk(): void {
    console.log('honk');
  }
}

50. Fields with Inheritance (4min)

  • NOTE: parent class receives 'color', so child class must pass this in
  • NOTE: WHEN CHILD CLASS DOES NOT HAVE CONSTRUCTOR -> class Car calls the parent's constructor
  • NOTE: WHEN CHILD CLASS HAS CONSTRUCTOR it needs to abide by its own constructor AND parent constructor via 'super()'
    • and pass in required props to super() for the parent
  • NOTE: because parent constructor has modifier specified, the child class DOES NOT define a modifier
    • you do not define modifier in child class because you want to use the parent's variable
class Vehicle {
  // color: string;
  // constructor(color: string) {
  //   this.color = color;
  // }

  constructor(public color: string) {} //simple syntax where instance variable is also created and assigned when value passed in

  public drive(): void {
    console.log('hello');
  }

  public honk(): void {
    console.log('honk');
  }
}

class Car extends Vehicle {
  constructor(public wheels: number, color: string) {
    super(color);
  }

  private drive(): void {
    console.log('vroom');
  }

  startDriving(): void {
    this.drive();
  }
}

const car = new Car(4, 'red');
car.drive();
car.honk();

51. Where to Use Classes (1min)

  • Classes allow strong code reuse


Section 09 - Design Patterns with Typescript

52. Updated Parcel Instructions (1min)

  • Parcel tool can be run without installation using npx
  • To build the app and run the development server, simply run the following command in your terminal:
npx parcel index.html

53. App Overview (3min)

section09-design-patterns-with-typescript-53.app-overview

  • project folder: /tutorial-stephengrider-typescript-02-designpatterns-googlemaps/
  • TODO: web app (browser) randomly generate (User OR Company) and show it on google map

54. Bundling with Parcel (5min)

section09-design-patterns-with-typescript-54.parcel.png

  • npm i -g parcel (note: install globally)
  • parcel will help get typscript working in browser (instead of ts-node)
    • NOTE: By default Parcel does not perform any code transpilation. This means that if you write your code using modern language features, that’s what Parcel will output. You can declare your app’s supported browsers using the browserslist field. When this field is declared, Parcel will transpile your code accordingly to ensure compatibility with your supported browsers.
  1. parcel reads project folder/index.html looks for <script> tag, sees its .ts (typescript)
  • NOTE: add type='module' to <script> tag ie. <script type="module" src="./src/index.ts"></script>
  1. converts the .ts to .js
  2. then updates the <script> to make use of the coverted .js instead of .ts

running the app

  • NOTE: package.json has scripts: but this is not working...
  • NOTE: Parcel has a development server built in, which will automatically rebuild your app as you make changes. To start it, run the parcel CLI pointing to your entry file
  • FIX: use commandline and run parcel pointing to starting file (from project directory): parcel index.html

V1 (DEPRECATED)

  • NOTE: v1 is no longer maintained -> https://v2.parceljs.org/getting-started/migration

    • FIX: uninstall: npm uninstall parcel-bundler (uninstall if you installed parcel-bundler)

V2

  • FIX: install: "parcel": "^2.0.0"
  • FIX: .cache folder replaced with .parcel-cache (add both to .gitignore)
  • NOTE: Parcel 2 matches browser behavior: classic <script> tags do not support imports or exports. Use a <script type="module"> element to reference a module.
  • Server running at http://localhost:1234

55. Project Structure (3min)

  • folder: tutorial-stephengrider-typescript-02-designpatterns-googlemaps/ section09-design-patterns-with-typescript-55.project-structure

  • index.ts

    • User.ts
    • Company.ts
    • Map.ts

56. IMPORTANT Info About Faker Installation (1min)

  • faker.js
  • https://fakerjs.dev/
  • currently v9
  • as of faker v6 typscript support is native and does not require installing of @types
  • To use this library, you can install it by running:
pnpm i @faker-js/faker --save-dev

faker usage

import { faker } from '@faker-js/faker';

57. Generating Random Data (5min)

  • folder: tutorial-stephengrider-typescript-02-designpatterns-googlemaps/
//src/User.ts
// import faker from 'faker';
import { faker } from '@faker-js/faker'; //forked version of faker import syntax

import { Mappable } from './CustomMap';

export class User implements Mappable {
  name: string;
  location: {
    lat: number;
    lng: number;
  };

  constructor() {
    this.name = faker.name.firstName();
    this.location = {
      lat: parseFloat(faker.address.latitude()),
      lng: parseFloat(faker.address.longitude()),

  }

  markerContent(): string {
    return `user Name: ${this.name}`;
  }
}

58. Type Definition Files (5min)

  • if you see the error Could not find the declaration module 'faker'
    • this happens because in your project you can import and use js libraries, but if your project is a typescript project, then it wants to know types for functions/variables. but if the library is a js library, typescript wont know/how to figure out the types..
  • FIX:
    • type definition files tell typescript function available inside library
    • can be included with library

58. type definition files

  • popular libraries have type definition files already created by definitelytyped that start with:
  • pnpm i @types/{library name}
  • start project parcel index.html

59. Using Type Definition Files (6min)

  • folder: tutorial-stephengrider-typescript-02-designpatterns-googlemaps/

  • with the type installed, on the import if you hold down CTRL (windows), you can click on the type definition class (index.d.ts)

  • no implemention only definition types

  • with class definition: class User{ location:{lat:number, lng:number}}

  • we are responsible for object initialization (in constructor) and properties inside eg. location = {}

//src/User.ts
export class User {
  name: string;
  location: {
    lat: number;
    lng: number;
  };

  constructor() {
    this.name = faker.name.firstName();
    this.location = {
      lat: parseFloat(faker.address.latitude()),
      lng: parseFloat(faker.address.longitude()),
    };
  }
}

60. Export Statements in Typescript (5min)

  • add export to class then import to use
  • in typescript, convention is to not use export default keyword
  • always use import with import {} from syntax
  • folder: tutorial-stephengrider-typescript-02-designpatterns-googlemaps/
//index.ts
import { User } from './User';

const user = new User();
console.log(user);

const map = new CustomMap('map');
map.addMarker(user);
//...

61. Defining a Company (5min)

  • folder: tutorial-stephengrider-typescript-02-designpatterns-googlemaps/
//src/Company.ts
import faker from 'faker';
import { Mappable } from './CustomMap';

export class Company implements Mappable {
  name: string;
  catchPhrase: string;
  location: {
    lat: number;
    lng: number;
  };

  constructor() {
    this.name = faker.company.companyName();
    this.catchPhrase = faker.company.catchPhrase();
    this.location = {
      lat: parseFloat(faker.address.latitude()),
      lng: parseFloat(faker.address.longitude()),
    };
  }

  markerContent(): string {
    return `
    <h1>Company Name: ${this.name}</h1>
    <h3>CatchPhrase ${this.catchPhrase}</h3>
    `;
  }
}
//index.ts
import { User } from './User';
import { Company } from './Company';

//...

const user = new User();
console.log(user);

const company = new Company();
console.log(company);

//...

62. Note on Generating an API Key (1min)

  • folder: tutorial-stephengrider-typescript-02-designpatterns-googlemaps/

  • next video shows how to hook up Google Maps by generating an API key on the Google Developer's Console

  • Creating an API key requires a Google Developer account with billing enabled.

  • This means you have to have a credit card tied to your Google account.

  • If you do not have a credit card tied to your Google account, or do not want to add one, then please use this pre-generated API key instead:

    • NOTE: key is given from course -> AIzaSyBNLrJhOMz6idD05pzfn5lhA-TAw-mAZCU

63. Adding Google Maps Support (8min)

section09-design-patterns-with-typescript-63.adding-googlemaps

steps

  1. create google project google developers console
  2. enable google maps
  • navigation menu (top-left) -> api + services -> library -> maps api -> maps javascript api -> enable
  1. generate api key
  • navigation menu (top-left) -> api + services -> credentials -> create credentials
  1. add google maps script tag to html (easiest way)
  • the url is https://maps.googleapis.com/maps/api/js?key=
<!-- index.html -->
<html>
  <body>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBNLrJhOMz6idD05pzfn5lhA-TAw-mAZCU&callback=Function.prototype"></script>
    <script type="module" src="./src/index.ts"></script>
  </body>
</html>
  1. check that it works

running the app

  • parcel index.html
  • browser dev-tools -> network -> filter (js) -> line js? should have status 200

64. Required Update for New @types Library (1min)

  • @types/googlemaps has been deprecated

  • UPDATE: install npm install @types/google.maps

  • you will still see a TS error in your code editor: Cannot find name 'google'.ts(2304)

  • FIX: the very first line in the index.ts file, you will need to add a triple slash directive:

//index.ts
/// <reference types="@types/google.maps" />

65. Google Maps Integration (4min)

  • when script is added directly to html instead of imports, it is added as a global variable.
  • DEPRECATED: we need to add @types @types/googlemaps
  • UPDATE: type definition file: @types/google.maps

testing type definition

  • you can use the type definition file to understand how google maps sdk works (CTRL + click on type definition import)
  • TODO: in a project file, type google then hover over and CTRL + click
  • NOTE: in the type definition declare namespace google.maps {} means we have access to a global variable google
  • browser console: should have access to google
declare namespace google.maps {}

fold code in vscode

  • folds code to just show function names
  • TODO: CTRL + SHIFT + P -> >fold level -> 2
//src/CustomMap.ts
export class CustomMap {
  private googleMap: google.maps.Map;

  constructor(divId: string) {
    this.googleMap = new google.maps.Map(
      document.getElementById(divId) as HTMLElement,
      {
        zoom: 1,
        center: {
          lat: 0,
          lng: 0,
        },
        draggable: false,
      }
    );
  }
  //...
}

66. Exploring Type Definition Files (3min)

  • TODO: showing User and Company as markers on the google map

67. Hiding Functionality (6min)

section09-design-patterns-with-typescript-67.hiding-functionality

  • we have been given access to the googlemaps api via google.maps.Map but we only need a small set of features it provides
  • TODO: restrict exposed methods of google.maps.Map api by creating our own class CustomMap with limited features available to other developers when using class

68. Why Use Private Modifiers? Here's Why (8min)

section09-design-patterns-with-typescript-68.private-modifiers.png

  • created CustomMap class which exposes google maps
  • NOTE: the private googleMap modifier
  • NOTE: we pass the id inside the constructor

good implementation

//src/CustomMap.ts
export interface Mappable {
  location: {
    lat: number;
    lng: number;
  };
  markerContent(): string;
}

export class CustomMap {
  private googleMap: google.maps.Map;

  constructor(divId: string) {
    this.googleMap = new google.maps.Map(
      document.getElementById(divId) as HTMLElement,
      {
        zoom: 1,
        center: {
          lat: 0,
          lng: 0,
        },
        draggable: false,
      }
    );
  }

  addMarker(mappable: Mappable): void {
    const marker = new google.maps.Marker({
      map: this.googleMap,
      position: {
        lat: mappable.location.lat,
        lng: mappable.location.lng,
      },
    });

    marker.addListener('click', () => {
      const infoWindow = new google.maps.InfoWindow({
        content: mappable.markerContent(),
      });

      infoWindow.open(this.googleMap, marker);
    });
  }
}
  • then later in index.ts, we access it like this
const map = new CustomMap('map');
map.addMarker(user);
map.addMarker(company);

69. Adding Markers (9min)

  • 2 solutions -> bad vs good solution

BAD code method

  • you have just seen how to do it the correct way, this is the BAD code method:
  • bad code -> methods to create marker
  • NB: when importing a Class, you can also reference it as a type
  • taking look at type definition file
  • NOTE: Marker() takes in an object with map property which references the googleMap property from the class: new google.maps.Marker({ map: this.googleMap});
  • NOTE: if you get a CustomMap.ts error like Cannot find namespace 'google'.ts(2503)
  • FIX: npm install --save-dev @types/google.maps
  • Update project root: tsconfig.json
//src/CustomMaps.ts
import { User } from './User';
import { Company } from './Company';

class CustomMap {

  private googleMap: google.maps.Map;

  constructor(){
    this.googleMap = //...
  }

  // NOTE: THIS IS THE BAD CODE
  addUserMarker(user: User): void {
    new google.maps.Marker({
      map: this.googleMap,
      position:{
        lat://...
        lng://...
      }
    });
  }
  addCompanyMarker(company: Company): void {

  }
}

70. Duplicate Code (3min)

  • Company class follows same format as User (above)
//src/index.ts

// good METHOD
const user = new User();
const company = new Company();
const map = new CustomMap('map');

map.addMarker(user);
map.addMarker(company);

//bad METHOD
const user = new User();
const company = new Company();
const map = new CustomMap('map');

map.addUserMarker(user);
map.addCompanyMarker(company);

71. One Possible Solution (7min)

  • create a more generic function name addUserMarker becomes addMarker
  • change the received prop from user to more generic mappable
  • NOTE: with User | Company, it is checking all properties that exist on both types
  • the downside is that only common properties exist and have to add import and | syntax (tight coupling)
// CustomMap.ts
addMarker(mappable: User | Company):void{
  new google.maps.Marker({
    map: this.googleMap,
    position:{
      lat: mappable.location.lat,
      lng: mappable.location.lng
    }
  });
}

72. Restricting Access with Interfaces (6min)

  • add an interface Mappable
  • classes implementing Mappable have to satisfy the properties listed in Mappable
//src/CustomMap.ts
export interface Mappable {
  location: {
    lat: number;
    lng: number;
  };
  markerContent(): string;
}
  • receives Mappable type
// CustomMap.ts
addMarker(mappable: Mappable):void{
  new google.maps.Marker({
    map: this.googleMap,
    position:{
      lat: mappable.location.lat,
      lng: mappable.location.lng
    }
  });
}

73. Implicit Type Checks (3min)

  • addMarker() receives type Mappable, and typescript just checks that whatever is passed in must implement Mappable ie it has the properties which satisfy Mappable
const map = new CustomMap('map');

map.addMarker(user);
map.addMarker(company);

74. Showing Popup Windows (7min)

  • added to addMarker() and ensure marker gets a 'click' listener
  • we only create the info window when the marker is clicked
export class CustomMap {
  //...

  addMarker(mappable: Mappable): void {
    const marker = new google.maps.Marker({
      map: this.googleMap,
      position: {
        lat: mappable.location.lat,
        lng: mappable.location.lng,
      },
    });

    marker.addListener('click', () => {
      const infoWindow = new google.maps.InfoWindow({
        content: mappable.markerContent(),
      });

      infoWindow.open(this.googleMap, marker);
    });
  }
}

75. Updating Interface Definitions (7min)

  • Mappable should be updated with a markerContent function
  • the object passed in to Mappable should have a markerContent function
//src/CustomMap.ts
export interface Mappable {
  location: {
    lat: number;
    lng: number;
  };
  markerContent(): string;
}
//src/User.ts
export class User implements Mappable {
  //...
  markerContent(): string {
    return `user Name: ${this.name}`;
  }
}
//src/Company.ts
export class Company implements Mappable {
  //...
  markerContent(): string {
    return `
    <div>
      <h1>Company Name: ${this.name}</h1>
      <p>Catchphrase: ${this.catchPhrase}</p>
    </div>
    `;
  }
}

76. Optional Implements Clauses (6min)

  • classes implementing interfaces should get implements keyword
  • ensure Mappable is exported
  • FIX: timport Mappable in User and after class definition add implements Mappable -fix allows typescript to let us know when we dont fully implement the interface
//User
import { Mappable } from './CustomMap';
class User implements Mappable {}

77. App Wrapup (8min)

  • wrap up! - just watch this video for refresher
  • interface definitions -> they describe how to work with the class
  • instead of creating an object type to work a class Definition, we say it should implement the interface
    • code reuse + low coupling between classes in the app

section09-design-patterns-with-typescript-77.app-wrapup.png


About

section25-typescript-basics-of-typescript (moved to its own repository)

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published