TypeScript

TypeScript est un langage de programmation à typage statique qui se compile en JavaScript. Cela signifie que vous devez définir les types de vos variables, fonctions et autres éléments de code, ce qui permet au compilateur de détecter les erreurs potentielles dès le stade de l'écriture du code.

TypeScript est un sur-ensemble de JavaScript, ce qui signifie que tout code JavaScript valide est également du code TypeScript valide. Cela signifie que vous pouvez utiliser TypeScript pour écrire du code JavaScript plus robuste et plus maintenable.

Exemple d’interface :

interface Article {
  title: string
  id: number
}

const article: Article = {
  title: "Hayes",
  id: 0,
}

Définition d’une propriété optionnelle :

interface Article {
  title: string
  id?: number
}

const article: Article = {
  title: "Hayes",
}

Type Vs Interface

La différence entre interface et type est un peu subtile, surtout depuis les dernières versions qui font converger leurs fonctionnalités. Néanmoins, les types sont un peu plus flexibles puisqu'ils peuvent être composés via des unions ou des intersections

type Article = {
  title: string
  id?: number
}

les types peuvent être combinés via une intersection  &:

type Person = {
  name: string
  age: number
}

type Employee = Person & {
  id: number
  department: string
}
// Employee: { name: string; age: number; id: number; department: string }

l'union permet de conditionner un type en énumérant plusieurs valeurs possibles :

type Color = "red" | "blue" | "green"

type Filter = {
  name: string
  email: string
  firstname: string
  lastname: string
}

type SortOptions = {
  sortBy: "name" | "email"
  sortOrder: "asc" | "desc"
}

function findUser(filter: Filter, sort?: SortOptions) {
  // ...
}

l'union est souvent utilisée pour signaler qu'une valeur peut être soit null, soit d'un type donné. Par exemple :

type Filter = {
  id: string | number
  name: string
  email: string
  firstname: string | null // Ici firstname peut être SOIT une string, SOIT null
  lastname: string | null // Idem pour lastname
}

L'opérateur keyof :

Il permet d'extraire les clés (ou propriétés) d'un type sous forme d'une union de chaînes de caractères. Ici, keyof Filter revient donc à écrire 'id' | 'name' | 'email' | 'firstname' | 'lastname'.

type SortOptions = {
  sortBy: keyof Filter
  sortOrder: "asc" | "desc"
}

Il existe un opérateur similaire à keyof, et il s'agit de key :

type User = {
  [key: string]: any
}

Cette notation permet de préciser qu'il peut y avoir une clé de n'importe quel nom (à condition que ce soit une chaîne de caractères) sur l'objet User. Le type any permet quant à lui d'indiquer que la valeur associée à cette clé peut être de n'importe quel type

Les tableaux

// tableau d'un seul et unique élément en string
type oneStringArray = [string]
// tableau de string sans limitation sur la taille
type stringArray = string[]
// ... qui peut également s'écrire ainsi :
type stringArray = Array<string>
type streamFormat = [string, number, string]
// tableau de format "stream" contenant une chaîne de caractères,
// un nombre et une autre chaîne de caractères
const test: streamFormat = ["Start", 123, "End"] //on appelle cela un tuple !

Les fonctions

interface CalculatorInterface {
  add: (x: number, y: number) => number
  subtract: (x: number, y: number) => number
}

class Calculator implements CalculatorInterface {
  add(x: number, y: number) {
    return x + y
  }

  subtract(x: number, y: number) {
    return x - y
  }
}

Il est également possible de typer les signatures de fonction de manière indépendante :

function greet(name: string): void {
  console.log(`Hello, ${name}!`)
}
//Dans cet exemple, la fonction greet prend un argument name de type string 
// et ne retourne rien (:void)

Les génériques

Voici un exemple de fonction générique qui prend un tableau de n'importe quel type et renvoie le premier élément de ce tableau :

function getFirstElement<T>(arr: T[]): T | undefined {
  if (arr.length > 0) {
    return arr[0]
  }
  return undefined
}

Dans cet exemple, le type générique T est utilisé pour représenter le type d'élément contenu dans le tableau. La fonction getFirstElement prend un tableau de type T[] en argument et renvoie le premier élément de ce tableau (qui est donc de type _T_) si le tableau n'est pas vide, undefined sinon (d'où le retour _T | undefined_).

On peut donc utiliser cette fonction avec différents types de tableaux :

const numbers = [1, 2, 3, 4]
const firstNumber = getFirstElement(numbers) // renvoie 1

const strings = ["foo", "bar", "baz"]
const firstString = getFirstElement(strings) // renvoie "foo"

Par exemple, Promise<unknown> désigne simplement une promesse qui résoudra sur une valeur de type unknown. Si la promesse se résout avec un number, vous l'aurez devinez, il suffit de l'écrire ainsi : Promise<number>.

Le type Unknown :

On peut affecter absolument ce qu'on veut à une variable de type unknown :

let value: unknown

value = undefined
value = null
value = 123
value = true
value = "Hello"
value = []
value = {}
value = () => {}

Le plus souvent, on va régler le linter de nos projets pour interdire l'usage de any implicites, afin justement d'éviter la confusion entre unknown et any : soit on affirme ne pas savoir (unknown), soit on affirme accepter “n'importe quoi” (any).