Skip to content

8. Les fonctions

Les scripts de ce chapitre se trouvent dans le dossier [fonctions] du projet.

8.1. script [func-01]

Ce script s'intéresse au mode de passage des paramètres d'une fonction : par valeur pour les nombres, chaînes et booléens ; par référence pour les tableaux, objets littéraux et fonctions.

'use strict';
// mode de passage des paramètres d'une fonction
// -----------------------nombre - passage par valeur
function doSomethingWithNumber(param: number): void {
  param++;
  console.log("[param inside function]=", param, "[type]=", typeof (param), "[passage par référence]=", param === count);
}
// code d'appel
let count: number = 10;
doSomethingWithNumber(count);
console.log("[count outside function]=", count);

// --------------------- chaîne - passage par valeur
function doSomethingWithString(param: string): void {
  param += " xyz"
  console.log("[param inside function]=", param, "[type]=", typeof (param), "[passage par référence]=", param === text);
}
// code d'appel
let text: string = "abcd";
doSomethingWithString(text);
console.log("[text outside function]=", text);

// --------------------- booléen - passage par valeur
function doSomethingWithBoolean(param: boolean): void {
  param = !param;
  console.log("[param inside function]=", param, "[type]=", typeof (param), "[passage par référence]=", param === bool);
}
// code d'appel
let bool: boolean = true;
doSomethingWithBoolean(bool);
console.log("bool [outside function]=", bool);

// --------------------- tableau - passage par référence
function doSomethingWithArray(param: number[]): void {
  param.push(1000);
  console.log("[param inside function]=", param, "[type]=", typeof (param), "[passage par référence]=", param === tab);
}
// code d'appel
const tab: number[] = [10, 20, 30];
doSomethingWithArray(tab);
console.log("[tab outside function]=", tab);

// --------------------- objet - passage par référence
// [any] pour pouvoir ajouter dynamiquement [unePropriétéNouvelle], comme en JS
function doSomethingWithObject(param: any): void {
  param.unePropriétéNouvelle = "xyz";
  console.log("[param inside function]=", param, "[type]=", typeof (param), "[passage par référence]=", param === obj);
}
// code d'appel
const obj: any = [10, 20, 30];
doSomethingWithObject(obj);
console.log("[obj outside function]=", obj);

// --------------------- fonction - passage par référence
// [any] car on attribue dynamiquement une propriété à la fonction elle-même (cf plus bas)
function doSomethingWithFunction(param: any): void {
  // une chose plutôt bizarre qui marche pourtant
  param.unePropriétéNouvelle = "xyz";
  console.log("[param inside function]=", param, "[type]=", typeof (param), "[passage par référence]=", param === f);
}
// code d'appel
const f: any = (x: number) => x + 4;
doSomethingWithFunction(f);
console.log("[f outside function]=", f, f.unePropriétéNouvelle, typeof (f));
  • les types primitifs (number, string, boolean) sont passés par valeur : modifier param à l'intérieur de la fonction ne modifie jamais la variable d'origine (passage par référence = false dans chaque cas) ;
  • les tableaux, objets et fonctions sont passés par référence : param === tab (ou obj, ou f) vaut true, et une modification à l'intérieur de la fonction est bien visible à l'extérieur ;
  • ligne 45 : function doSomethingWithObject(param: any) — typé any car on ajoute dynamiquement une propriété (unePropriétéNouvelle) non prévue dans le type d'origine du tableau obj ;
  • ligne 58 : même chose pour doSomethingWithFunction — on ajoute une propriété à la fonction elle-même, un comportement JavaScript un peu déroutant mais parfaitement valide (une fonction est aussi un objet).
npx tsx fonctions/func-01.ts

Résultat de l'exécution :

[param inside function]= 11 [type]= number [passage par référence]= false
[count outside function]= 10
[param inside function]= abcd xyz [type]= string [passage par référence]= false
[text outside function]= abcd
[param inside function]= false [type]= boolean [passage par référence]= false
bool [outside function]= true
[param inside function]= [ 10, 20, 30, 1000 ] [type]= object [passage par référence]= true
[tab outside function]= [ 10, 20, 30, 1000 ]
[param inside function]= [ 10, 20, 30, 'unePropriétéNouvelle': 'xyz' ] [type]= object [passage par référence]= true
[obj outside function]= [ 10, 20, 30, 'unePropriétéNouvelle': 'xyz' ]
[param inside function]= [Function: f] { 'unePropriétéNouvelle': 'xyz' } [type]= function [passage par référence]= true
[f outside function]= [Function: f] { 'unePropriétéNouvelle': 'xyz' } xyz function

8.2. script [func-02]

Ce script montre que [function] est un type de donnée comme un autre : une variable peut avoir ce type, et il existe deux façons de définir une fonction — avec le mot clé function, ou avec la notation fléchée =>.

'use strict';
// on peut affecter une fonction à une variable
const variable1: (a: number, b: number) => number = function (a, b) {
  return a + b;
};
console.log("typeof(variable1)=", typeof (variable1));
// la variable peut ensuite s'utiliser comme une fonction
console.log("variable1(10,12)=", variable1(10, 12));
// la définition de la fonction peut se faire avec la notation =>
const variable2: (a: number, b: number, c: number) => number = (a, b, c) => {
  return a - b + c;
};
console.log("variable2(10,12,14)=", variable2(10, 12, 14));
// on peut ne pas mettre les accolades s'il n'y a qu'une expression dans le code de la fonction
// cette expression est alors la valeur de retour de la fonction
const variable3 = (a: number, b: number, c: number): number => a + b + c;
console.log("variable3(10,12,14)=", variable3(10, 12, 14));
  • ligne 3 : const variable1: (a: number, b: number) => number = function (a, b) {...} — le type d'une variable-fonction se déclare comme une signature (paramètres) => typeDeRetour ;
  • lignes 10-12 : la même chose avec la notation fléchée (a, b, c) => { ... } ;
  • ligne 16 : sans accolades, le corps de la fonction se réduit à une seule expression, qui devient automatiquement la valeur de retour — (a, b, c): number => a + b + c.
npx tsx fonctions/func-02.ts

Résultat de l'exécution :

1
2
3
4
typeof(variable1)= function
variable1(10,12)= 22
variable2(10,12,14)= 12
variable3(10,12,14)= 36

8.3. script [func-03]

Ce script montre qu'une fonction peut être passée en paramètre à une autre fonction — un procédé très utilisé dans les frameworks TypeScript/JavaScript (callbacks, map/filter/reduce du chapitre « Les tableaux »...) :

'use strict';
// les paramètres d'une fonction peuvent être de type [fonction]

// fonction f1
function f1(param1: number, param2: number): number {
  return param1 + param2 + 10;
}
// fonction f2
function f2(param1: number, param2: number): number {
  return param1 + param2 + 20;
}
// fonction g avec une fonction f en paramètre
function g(param1: number, param2: number, f: (a: number, b: number) => number): number {
  return f(param1, param2) + 100;
}
// utilisations de g
console.log(g(0, 10, f1));
console.log(g(0, 10, f2));
// le paramètre effectif de type fonction peut être passé en direct - forme 1
console.log(g(0, 10, (param1, param2) => {
  return param1 + param2 + 30;
}));
// le paramètre effectif de type fonction peut être passé en direct - forme 2
console.log(g(0, 10, function (param1, param2) {
  return param1 + param2 + 40;
}));
  • ligne 13 : function g(param1: number, param2: number, f: (a: number, b: number) => number): number — le 3ᵉ paramètre f est lui-même typé comme une fonction ;
  • lignes 20-22 et 24-26 : le paramètre effectif de type fonction peut être passé directement, sous forme fléchée ou classique, sans être d'abord stocké dans une variable.
npx tsx fonctions/func-03.ts

Résultat de l'exécution :

1
2
3
4
120
130
140
150

8.4. script [func-04]

Ce script montre qu'une fonction peut se comporter comme une classe — un mécanisme antérieur à ES6 mais toujours valide, bien que remplacé aujourd'hui par le mot clé class (voir chapitre « Les classes ») :

'use strict';
// une fonction peut être utilisée comme un objet

// une coquille vide
// [any] : le type de [this] est ici volontairement souple, car on attribue
// des propriétés à la fonction elle-même APRÈS sa définition (cf plus bas) -
// un schéma que TypeScript ne peut pas déduire tout seul
function f(this: any): void {

}
// à qui on attribue des propriétés de l'extérieur
(f as any).prop1 = "val1";
(f as any).show = function (this: any) {
  console.log(this.prop1);
};
// utilisation de f
(f as any).show();

// une fonction g fonctionnant comme une classe
function g(this: any): void {
  this.prop2 = "val2";
  this.show = function (this: any) {
    console.log(this.prop2);
  };
}
// instanciation de la fonction avec [new]
new (g as any)().show();
  • lignes 5-10 : f est une fonction « coquille vide », typée this: any — cette annotation spéciale du premier paramètre indique à TypeScript le type de this à l'intérieur de la fonction, nécessaire ici car des propriétés lui sont ajoutées de l'extérieur, un schéma que TypeScript ne peut pas déduire seul ;
  • lignes 12-15 : on donne, depuis l'extérieur, des propriétés à f(f as any).prop1 = ... ;
  • ligne 17 : on appelle f.show(), et non f() — c'est bien la notation d'utilisation d'un objet, pas d'un appel de fonction classique ;
  • lignes 20-25 : g définit this.prop2 et this.show en son sein, comme le ferait un constructeur de classe ;
  • ligne 27 : new (g as any)() instancie g comme une classe.
npx tsx fonctions/func-04.ts

Résultat de l'exécution :

val1
val2
Remarque : ES6 a introduit le mot clé class (voir chapitre « Les classes »), qui permet d'obtenir directement ce résultat sans passer par ce détour fonctionnel.

8.5. script [func-05]

Ce script montre l'usage du [rest operator] (...), qui rassemble tous les arguments restants d'un appel dans un tableau :

'use strict';
// rest operator
function f(arg1: number, ...otherArgs: unknown[]): void {
  // 1er argument
  console.log("arg1=", arg1);
  // les autres arguments
  let i = 0;
  otherArgs.forEach(element => {
    console.log("otherArguments[", i, "]=", element);
    i++;
  });
}

// appel
f(1, "deux", "trois", { x: 2, y: 3 })
  • ligne 3 : function f(arg1: number, ...otherArgs: unknown[]): voidotherArgs rassemble tous les arguments passés après arg1, quel que soit leur nombre. Le type unknown[] est adapté ici puisque otherArgs mélange volontairement des types différents (chaînes et objet, dans l'appel de la ligne 15).
npx tsx fonctions/func-05.ts

Résultat de l'exécution :

1
2
3
4
arg1= 1
otherArguments[ 0 ]= deux
otherArguments[ 1 ]= trois
otherArguments[ 2 ]= { x: 2, y: 3 }