TypeScript

TypeScript

introductie

TypeScript?

TypeScript is JavaScript with syntax for types.
It is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.

TypeScript?

TypeScript is JavaScript with syntax for types.
It is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.

 

  • Superset (~ uitbreiding) van JavaScript
    • Geldige JavaScript-code  Geldige TypeScript-code (syntactisch)
    • Opgepast: niet in omgekeerde richting toepasbaar
  • TypeScript compileert naar JavaScript

TypeScript

  • Strongly/statically typed languages
    • data types are defined at compile-time and
      can not change
    • e.g. TypeScript, Java, C#, Rust ...
  • Loosely/dynamically typed languages
    • data types are evaluated at run-time and
      can change
    • e.g. JavaScript, Python, Ruby, PHP ...

TypeScript

let temperature = 20;
// wat is het type van deze variabele 'temperature' op lijn 1?
temperature = "warm";
// wat is nu het type van deze variabele?
let temperature: number = 20;
// wat is het type van deze variabele 'temperature' op lijn 1?
temperature = "warm";
// TypeError

JavaScript

TypeScript

Waarom TypeScript?

  • Voordelen
    • Bugs ontdekken vóór code wordt uitgevoerd
    • Expliciete code is vaak makkelijker leesbaar
    • Betere code completion in IDE
    • Bijkomende features t.o.v. JavaScript

Waarom TypeScript?

  • Nadelen
    • Compilatie-stap: vergt tijd en resources
    • Meer code nodig voor dezelfde uitkomst
    • Minder flexibiliteit dan JavaScript
    • Discipline nodig, anders wordt het een boeltje !

Waarom TypeScript?

let temperature = 20;

// Developer #10 schrijft de lijn hieronder
temperature = "warm";

// Maar heeft deze conversie over het hoofd gezien ...
const temperatureInFahrenheit = (temperature * 1.8) + 32;

// Bonus: wat is de waarde van temperatureInFahrenheit nu?

Type(s in Java)Script

  • Welke 8 types zijn er in JavaScript?
    • ​Niet spieken!
      We doen een rondje, elk om beurt.

Type(s in Java)Script

  • Welke 8 types zijn er in JavaScript?
    • null
    • undefined
    • number
    • boolean
    • string
    • object
    • BigInt
    • Symbol

TypeScript

Werken met TypeScript

Werken met TypeScript

Werken met TypeScript

  • Nieuw project?
    Installeer, initialiseer & compileer
cd my-empty-folder
npm init -y
npm install typescript --save-dev
npx tsc --init
npx tsc

Werken met TypeScript

  • Extensie:
    .js → .ts
  • Variabele declareren/initialiseren?
    Definieer het type!
let temperature: number = 20;
let greeting: string = "Hello, students!";
let isThisCourseAwesome: boolean;

Werken met TypeScript

  • Functies
    • Parameter type annotations
    • Return type annotations
function sum(a: number, b: number): number {
  return a + b;
}

Werken met TypeScript

  • Functies: parameter type + return type
    • Als de return value van een functie niet gebruikt wordt, dan noemen we de return type void
function someFunc(): void {
  console.log("Hi there!");
}
  • Onthou wel: elke functie in Javascript heeft een impliciete return value. Welke waarde is dat?

Werken met TypeScript

type Address = {
  name: string;
  postalCode: string;
  city: string;
  street: string;
  houseNumber: number;
};

async function sendMail(address: Address): void {
  // some logic to send physical mail goes here ...
}

// What errors will the compiler throw here?
sendMail({
  postalCode: "9000",
  city: "Ghent",
  street: "Leeuwstraat",
  houseNumber: "1",
});

Werken met TypeScript

// Who needs postal codes, right?
type Address = {
  name: string;
  postalCode?: string;
  city: string;
  street: string;
  houseNumber: number;
};

async function sendMail(address: Address): void {
  // some logic to send physical mail goes here ...
}

sendMail({
  name: "Karel De Smet",
  city: "Ghent",
  street: "Leeuwstraat",
  houseNumber: 1,
});

Werken met TypeScript

function greeting(firstName: string, lastName?: string): void {
  const nameToPrint = lastName ? `${firstName} ${lastName}` : firstName;
  console.log(`Hello ${nameToPrint}!`);
}

// We can just greet people by their first name, the last name is optional
greeting("Karel");
greeting("Karel", "De Smet");

Werken met TypeScript

function greet(name, greeting = 'Hello') {
  console.log(greeting + ', ' + name);
}

greet("Karel");
// "Hello, Karel"

greet("John", "Hi");
// "Hi, John"

Werken met TypeScript

// Basically the same as type Address from the previous slides
interface Address {
  name: string;
  postalCode?: string;
  city: string;
  street: string;
  houseNumber: number;
};

Werken met TypeScript

function sum(arr: Array<number>): number {
  return arr.reduce((a: number, b: number) => {
	return a + b;
  }, 0);
}

// Array<number> en number[] betekenen exact hetzelfde,
// je kiest zelf welke syntax je verkiest

Werken met TypeScript

  • Union type
    • Hebben de array values mogelijk een verschillend type?
      Dat lossen we op met een union type (|)
const arrayOfNumberOrString: Array<number | string> = [];
arrayOfNumberOrString.push(1);
arrayOfNumberOrString.push("Hello world");
// Error: Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
arrayOfNumberOrString.push(false);

Werken met TypeScript

  • Tuples
    • Ken je de lengte van de array en weet je ook welk type van toepassing is op elke index? Dan spreken we van een tuple.
const stringNumberTuple: [string, number] = ["Weeks from shore", 2];

Werken met TypeScript

  • Literal type
    • Wat als je het type van een variabele wil beperken tot
      bv. een aantal specifieke strings?
type Theme = "dark" | "light";

// Error: Type '"violet"' is not assignable to type 'Theme'.
const theme: Theme = "violet";

Oefeningen

Oefeningen

Oefening 1:

  • Declare and initialize the following variables with explicit types:
    • username (string)
    • age (number)
    • isStudent (boolean)
    • courses (array of strings)
  • Log all of them to the console

Oefeningen

Oefening 2:

  • Write a function multiply that takes two arguments of type number
    and returns their product.

Oefeningen

Oefening 3:

  • Create an interface or type User with the following properties:
    • username (string)
    • email (string)
    • age (number, optional)
    • isActive (boolean)
  • Then create a function printUserInfo that takes a User object
    and logs the user’s information to the console.
     

Oefeningen

Oefening 4:

  • Create an interface or type Product with properties name, price, and category. Define logical types for each property.
  • Next, create an array of Product objects and write a function getTotalPrice that calculates the total price of all products in the array.

Oefeningen

Oefening 5:

  • Write a function formatInput that takes a string or number as an argument.
    • If the argument is a number, return it as a string with a $ symbol in front (e.g., 100 -> "$100").
    • If the argument is a string, just return it in uppercase.

Oefeningen

TypeScript

More TypeScript!

Type inference

  • If you don't annotate the type,
    TypeScript will infer it, hence type inference
  • Preferred for primitives
  • Usually not advisable for object types, arrays ...
let greeting = "Hello world";
// The TypeScript compiler infers that greeting is of type string. 
// There's no need to make it explicit.

const arrayOfStudents = [];
// TypeScript can not know what type of values we will store in this array.
// So it infers this to be of type any[], which is almost never a good thing ...

Type inference

  • How can we see what TypeScript infers?
    Just hover over the variable in VS Code!
  • ⚠️ If we use const to initialize a variable,
    TypeScript will infer it as a literal type

Higher-order functions

  • We talked about type annotation of functions,
    ​but what about higher-order functions?
    • Uh... what is a higher-order function?

Higher-order functions

// We know how to annotate the sum function in TypeScript
function sum(a, b) {
  return a + b;
}

// But how do we annotate the add function?
function add(a) {
  return function(b) {
    return sum(a, b);
  }
}

const addSeven = add(7);
console.log(addSeven(3));
// 10

Higher-order functions

// Solution to the previous slide:
function sum(a: number, b: number): number {
  return a + b;
}

function add(a: number): (b: number) => number {
  return function(b: number) {
    return sum(a, b);
  }
}

const addSeven = add(7);
console.log(addSeven(3));
// 10

First-class functions

/* JavaScript has first-class functions, so we can encounter them anywhere
e.g. in React props

Exercise: copy-paste this in the TypeScript playground 
and add the missing interface */

interface Props {
  // ?
}


const props: Props = {
  onClick: () => console.log("Hi there!"),
  onSave: (data: string) => doSomethingWithDataAndReturnBoolean(data)
}

function doSomethingWithDataAndReturnBoolean(data: string): boolean {
  // This is just a mock implementation ...
  return true;
}

First-class functions

// Solution to the previous slide

interface Props {
  onClick: () => void;
  onSave: (data: string) => boolean
}

const props: Props = {
  onClick: () => console.log("Hi there!"),
  onSave: (data: string) => doSomethingWithDataAndReturnBoolean(data)
}

function doSomethingWithDataAndReturnBoolean(data: string): boolean {
  // This is just a mock implementation ...
  return true;
}

any

  • any turns off type checking completely
  • Not recommended without good reason
  • Code smell
let greeting: any = [];
// Since we annotated greeting with any, we can change the type later on 
// to anything else: string, number, boolean, array, object ...
// We can also call any method on it, or access any property, 
// even if that method or property does not exist

greeting.say();
greeting.toUpperCase();
// At compile time everything is OK. At run time, this will cause an error.

unknown

let vAny: any = 10;          // We can assign anything to any
let vUnknown: unknown =  10; // We can assign anything to unknown just like any 


let s1: string = vAny;     // Any is assignable to anything 
let s2: string = vUnknown; // Invalid; we can't assign vUnknown to any other type

vAny.method();     // Ok; anything goes with any
vUnknown.method(); // Not ok; we don't know anything about this variable
// Source: https://stackoverflow.com/a/51439876

unknown

  • Useful when accepting multiple types as arguments
  • Better than any ...
function prettyPrint(x: unknown): string {
  if (Array.isArray(x)) {
    return "[" + x.map(prettyPrint).join(", ") + "]";
  }
  if (typeof x === "string") {
    return `"${x}"`;
  }
  if (typeof x === "number") {
    return String(x);
  } 
  return "etc.";
}
// Source: https://blog.logrocket.com/when-to-use-never-unknown-typescript

Asynchronous code

// What is the return type of the function below?
async function getSomeData() {
  return fetch("https://some-data.com/api/data");
}

Asynchronous code

// Answer to question on the previous slide
async function getSomeData(): Promise<Response> {
  return fetch("https://some-data.com/api/data");
}

TypeScript comes with a ton of built-in types
e.g. for promises, events ...

Asynchronous code

// What will be the return type of the function below?
function getSomePromise() {
  return new Promise(() => {
    return getRandomNumber();
  });
}

function getRandomNumber(): number {
  return Math.random();
}

Asynchronous code

/* If we don't pass a type to the generic Promise constructor,
the Typescript compiler will infer it as Promise<unknown>.
The compiler is flawed here: it can't infer the Promise type (number).
*/ 
function getSomePromise(): Promise<unknown> {
  return new Promise(() => {
    return getRandomNumber();
  });
}

function getRandomNumber(): number {
  return Math.random();
}

Asynchronous code

/* 
But if we help the Typescript compiler out, 
it can infer it as Promise<number>
*/
function getSomePromise() {
  return new Promise<number>(() => {
    return getRandomNumber();
  });
}

function getRandomNumber(): number {
  return Math.random();
}

Extra

Extra

TypeScript

TypeScript with React/Next.js

TypeScript + React/Next.js

  • You're an expert at TypeScript now. Great!
  • But how do you use it with React/Next.js?
    • React
      • Props
      • Hooks
      • Events
      • Children
      • Styles

TypeScript + React/Next.js

TypeScript + React/Next.js

  • You're an expert at TypeScript now. Great!
  • But how do you use it with React/Next.js?
    • Next.js:
      • All that applies to React
      • VS Code plugin
      • Object Relational Mapper (ORM)

TypeScript + React/Next.js

function Button({ label, handleClick }) {
  return <button className="btn btn-large whatever" onClick={handleClick}>{label}</button>;
}
  • Exercise:
    • Create a new React project (with Vite)
    • Add and use TypeScript (documentation)
    • Create the component above and use it in App
    • Add type annotations for the props

TypeScript + React/Next.js

interface Props {
  label: string;
  handleClick: () => number;
}

function Button({ label, handleClick }: Props) {
  return <button className="btn btn-large whatever" onClick={handleClick}>
    {label}
  </button>;
}

export default Button;

PGM5/1 - TypeScript

By kareldesmet

PGM5/1 - TypeScript

  • 165