Fonctions, objets et tableaux
TypeScript 101, Télécom Paris
Trois questions sur la séance 1, tirées des QCM du cours.
Qu’affiche ce code ?
Bonjour AliceBonjour $nomBonjour undefinedB. Sans accolades, $nom est du texte ordinaire : il faut ${nom}.
Ce code s’exécute sans erreur, et rien n’apparaît dans la page. Pourquoi ?
textContent ne fonctionne pas sur un <li>innerHTMLB. Sans appendChild ou prepend, l’élément reste détaché.
moduleMath.ts contient const pi = 3.14159;, sans export.
pi vaut 3.14159pipi vaut undefinedpi est importé en lecture seuleB. Ce qui ne porte pas export reste invisible hors du fichier.
map, filter, find, reduceEn C, une fonction est une entité à part. En TypeScript, c’est une valeur : elle se range dans une variable, se passe en paramètre, se renvoie en résultat.
Trois mots pour la même chose : fonction fléchée, fonction anonyme, lambda. La syntaxe générale : (paramètres) => corps.
Les accolades délimitent un bloc : plusieurs lignes, variables locales, if, boucles.
Rien n’est renvoyé automatiquement. Il faut écrire return.
Une seule expression, dont la valeur est renvoyée implicitement.
| Instructions | Expression | |
|---|---|---|
| Écriture | => { ... } |
=> expression |
| Lignes | autant qu’on veut | une seule |
| Retour | explicite, return |
implicite |
Sans return |
renvoie undefined |
impossible d’oublier |
Les accolades ouvrent un bloc : x * 2 est calculé, puis jeté. Deux corrections :
Annoter le type de retour transforme cette erreur silencieuse en erreur de compilation. C’est la meilleure raison de le faire.
Lisez (a: number, b: number) => number comme une signature : « deux nombres en entrée, un nombre en sortie ». N’importe quelle fonction de cette forme convient.
int (*op)(int, int) est l’ancêtre direct de (a: number, b: number) => number.
Deux différences :
Le compilateur vérifie la signature entière, pas seulement le nombre de paramètres.
Durées en millisecondes. setInterval ne s’arrête jamais seul : gardez sa valeur de retour pour clearInterval.
setTimeout(maFonction(), 1000) appelle la fonction tout de suite. Écrivez setTimeout(maFonction, 1000), sans parenthèses. Même règle pour addEventListener.
saluer ne salue personne : elle fabrique une fonction qui, elle, saluera. La valeur de nom reste attachée à la fonction produite.
C’est une fermeture (closure) : la fonction capture son environnement.
Règle suffisante pour ce cours : interface pour un objet, type pour tout le reste (unions, littéraux, tuples).
readonlyinterface Produit {
readonly reference: string; // fixée à la création
nom: string;
promotion?: number; // peut être absente : number | undefined
}
const clavier: Produit = { reference: "KB-01", nom: "Clavier" };
clavier.reference = "KB-02"; // ERREUR : Cannot assign to 'reference'
const remise = (clavier.promotion ?? 0) * 0.5; // ?? fournit une valeur de repliconst protège la variable, readonly protège la propriété.
Seule la forme compte, pas le nom.
function presenter(p: Personne): string { return `${p.nom}, ${p.age} ans`; }
const etudiant = { nom: "Eve", age: 22, ecole: "Télécom Paris" };
presenter(etudiant); // accepté : etudiant a au moins nom et age
const eve: Personne = { nom: "Eve", age: 22, ville: "Paris" };
// ERREUR : Object literal may only specify known propertiesUne propriété en trop n’est refusée que dans un littéral écrit à la main : c’est presque toujours une faute de frappe.
...livre recopie toutes les propriétés, puis prix est remplacé{ prix: 9.9, ...livre } garde l’ancien prixLivre[] : un tableau d’objets, la forme de données la plus courante. C’est ce que la suite de la séance apprend à parcourir.
| C | TypeScript |
|---|---|
struct Personne { ... }; |
interface Personne { ... } |
struct Personne q = p; (copie) |
const q = { ...p }; |
struct Personne *r = &p; (même objet) |
const r = p; |
En C, affecter une struct copie ses champs. En TypeScript, affecter un objet ne copie rien.
Trois occasions de se tromper (i, la borne, la taille de destination) pour une idée très simple.
Vous fournissez une fonction, le tableau se charge du parcours.
interface Produit { nom: string; categorie: string; prix: number; stock: number; }
const produits: Produit[] = [
{ nom: "Clavier", categorie: "informatique", prix: 45, stock: 12 },
{ nom: "Souris", categorie: "informatique", prix: 20, stock: 0 },
{ nom: "Cahier", categorie: "papeterie", prix: 3, stock: 150 },
{ nom: "Stylo", categorie: "papeterie", prix: 1.5, stock: 300 },
{ nom: "Écran", categorie: "informatique", prix: 180, stock: 4 },
{ nom: "Agenda", categorie: "papeterie", prix: 12, stock: 0 },
];Un tableau d’objets : la forme de données la plus courante, et celle des évaluations.
forEach et mapmap ne modifie pas l’original : une ligne map sans const à gauche est presque toujours un bug. Pour renvoyer un objet : ({ ... }), avec les parenthèses.
filter et findfilter : un tamis, les éléments retenus restent intactsfind : le premier qui convient, ou undefinedreduceVous parcourez le tableau avec un accumulateur dans la main.
| Étape | acc avant |
p |
acc après |
|---|---|---|---|
| départ | 0 | ||
| 1 | 0 | Clavier, 45 × 12 | 540 |
| 2 | 540 | Souris, 20 × 0 | 540 |
| 3 | 540 | Cahier, 3 × 150 | 990 |
| … | … | ||
| 6 | 2160 | Agenda, 12 × 0 | 2160 |
La valeur initiale 0 fixe le type de l’accumulateur.
Je veux obtenir…
├── rien : j'agis sur chaque élément → forEach
├── un tableau
│ ├── même longueur, éléments transformés → map
│ └── plus court, éléments intacts → filter
└── une seule valeur
├── un élément : le premier qui convient → find
└── un résumé : somme, max, texte → reduce
Transformer : map. Garder : filter. Un seul : find. Résumer : reduce.
| Étape | Renvoie | Type |
|---|---|---|
produits |
6 produits | Produit[] |
.filter(papeterie) |
Cahier, Stylo, Agenda | Produit[] |
.filter(en stock) |
Cahier, Stylo | Produit[] |
.map(texte) |
["Cahier (3 €)", "Stylo (1.5 €)"] |
string[] |
.join(", ") |
"Cahier (3 €), Stylo (1.5 €)" |
string |
Suivez le type d’un maillon à l’autre. En cas de doute, coupez la chaîne en constantes et affichez-les.
| Piège | Symptôme |
|---|---|
find sans tester undefined |
'x' is possibly 'undefined' |
reduce sans valeur initiale |
l’accumulateur devient un Produit |
map((p) => { p.prix * 2; }) |
void[] : il manque le return |
const r = tab.forEach(...) |
r vaut undefined |
`${produits}` |
[object Object],[object Object],... |
Pour afficher un tableau d’objets : produits.map((p) => p.nom).join(", ").
Parcours essentiel, dans l’ordre
Ensuite
readonly (entraînement)Les exercices « transfert » sont les plus proches de ce que demandent les évaluations.