TypeScript : Créer des types à partir de types

Bonjour à tous, futurs maîtres de TypeScript ! Je suis ravi de vous guider sur ce voyage passionnant à travers le monde des types TypeScript. En tant qu'enseignant en informatique avec des années d'expérience, j'ai vu de visu comment la compréhension des types peut transformer un débutant en magicien de la programmation. Alors, embarquons dans cette aventure ensemble !

TypeScript - Creating Types from Types

Types Union

Imaginez que vous êtes dans une pâtisserie à glace, et que vous pouvez choisir soit du chocolat, soit de la vanille. En TypeScript, on appelle ce genre de choix un "type union". C'est comme dire : "Je veux soit cela, soit cela."

Voyons un peu de code :

type Flavor = "chocolate" | "vanille";

let myIceCream: Flavor;
myIceCream = "chocolate"; // C'est correct
myIceCream = "fraise"; // Erreur ! Le type '"fraise"' n'est pas assignable au type 'Flavor'.

Dans cet exemple, Flavor est un type union qui peut être soit "chocolate", soit "vanille". Lorsque nous essayons d'assigner "fraise" à myIceCream, TypeScript dit : "Attendez ! Cela n'est pas au menu !"

Les types union sont super utiles lorsque vous souhaitez limiter les valeurs possibles qu'une variable peut avoir. Voici un autre exemple :

type Answer = "yes" | "no" | "maybe";

function respondToQuestion(answer: Answer) {
if (answer === "yes") {
console.log("Super !");
} else if (answer === "no") {
console.log("Oh non !");
} else {
console.log("Je vois que vous êtes indécis.");
}
}

respondToQuestion("yes"); // Ça fonctionne bien
respondToQuestion("perhaps"); // Erreur ! L'argument de type '"perhaps"' n'est pas assignable au paramètre de type 'Answer'.

Types Intersection

Maintenant, parlons des types intersection. Si les types union sont sur "soit ceci, soit cela", les types intersection sont sur "soit ceci, soit cela". C'est comme commander un menu complet dans un restaurant à service rapide - vous obtenez le burger, les frites et le boisson.

Voici comment cela fonctionne dans le code :

type Name = {
firstName: string;
lastName: string;
};

type Age = {
age: number;
};

type Person = Name & Age;

let john: Person = {
firstName: "John",
lastName: "Doe",
age: 30
};

Dans cet exemple, Person est un type intersection qui combine Name et Age. C'est comme dire : "Une personne doit avoir un prénom, un nom et un âge."

Voici un autre exemple fun :

type Swimmer = {
swim: () => void;
};

type Flyer = {
fly: () => void;
};

type FlyingFish = Swimmer & Flyer;

let nemo: FlyingFish = {
swim: () => console.log("Nageant dans l'océan"),
fly: () => console.log("Volant au-dessus des vagues")
};

nemo.swim(); // Sortie : Nageant dans l'océan
nemo.fly();  // Sortie : Volant au-dessus des vagues

Types Utilitaires

TypeScript comes avec un ensemble de types utilitaires qui nous aident à manipuler et transformer des types existants. Ils sont comme le couteau suisse des types ! Jetons un œil aux plus couramment utilisés :

Partial

Partial<T> rend toutes les propriétés d'un type optionnelles. C'est comme dire : "Je veux tous cela, mais je n'ai pas besoin de tout cela dès maintenant."

interface Todo {
title: string;
description: string;
completed: boolean;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}

const myTodo = {
title: "Apprendre TypeScript",
description: "Étudier pendant 2 heures",
completed: false
};

const updatedTodo = updateTodo(myTodo, {
description: "Étudier pendant 3 heures"
});

console.log(updatedTodo);
// Sortie : { title: "Apprendre TypeScript", description: "Étudier pendant 3 heures", completed: false }

Pick<T, K>

Pick<T, K> construit un type en choisissant l'ensemble des propriétés K de T. C'est comme cueillir vos fruits préférés dans un panier de fruits.

interface User {
id: number;
name: string;
email: string;
age: number;
}

type UserSummary = Pick<User, "id" | "name">;

const userSummary: UserSummary = {
id: 1,
name: "John Doe"
};

Omit<T, K>

Omit<T, K> construit un type en choisissant toutes les propriétés de T puis en supprimant K. C'est l'inverse de Pick.

interface Product {
id: number;
name: string;
price: number;
description: string;
}

type ProductPreview = Omit<Product, "description">;

const productPreview: ProductPreview = {
id: 1,
name: "Gadget cool",
price: 99.99
};

Voici un tableau résumant ces types utilitaires :

Type Utilitaire Description
Partial Rend toutes les propriétés dans T optionnelles
Pick<T, K> Construit un type en choisissant l'ensemble des propriétés K de T
Omit<T, K> Construit un type en choisissant toutes les propriétés de T puis en supprimant K

Opérateur typeof

L'opérateur de type typeof en TypeScript nous permet de créer un type basé sur la forme d'une valeur existante. C'est comme prendre un moule d'un objet pour créer plus d'objets avec la même forme.

Voyons un exemple :

const user = {
name: "John",
age: 30,
location: "New York"
};

type User = typeof user;

const anotherUser: User = {
name: "Jane",
age: 25,
location: "London"
};

Dans cet exemple, nous utilisons typeof user pour créer un nouveau type User qui correspond à la forme de notre objet user. Cela est incroyablement utile lorsque vous souhaitez vous assurer que plusieurs objets ont la même structure.

Voici un autre exemple où typeof est pratique :

const colors = ["red", "green", "blue"] as const;
type Color = typeof colors[number];

let myColor: Color = "red"; // OK
myColor = "yellow"; // Erreur : Le type '"yellow"' n'est pas assignable au type '"red" | "green" | "blue"'.

Dans ce cas, nous utilisons typeof avec les types d'accès indexés pour créer un type union à partir d'un tableau de constantes.

Et voilà, les amis ! Nous avons parcouru le territoire des types TypeScript, en créant de nouveaux types à partir de ceux existants. Souvenez-vous, la pratique rend parfait, donc n'ayez pas peur d'expérimenter avec ces concepts dans vos propres projets. Bon codage, et puissent les types toujours être en votre faveur !

Credits: Image by storyset