Objets et Structures de Données
Utiliser des "getters" et "setters"
TypeScript supporte la syntaxe getter/setter.
L'utilisation de getters et de setters pour accéder aux données d'objets qui englobent le comportement pourrait être meilleure que la simple recherche d'un attribut sur un objet. "Pourquoi?" vous vous demander. Eh bien, voici une liste de raisons:
- Lorsque vous voulez faire plus que d'obtenir l’attribut d'un objet, vous n'avez pas besoin de rechercher et de modifier chaque accesseur dans votre base de code.
- Valide de façon plus simple en utilisant le mot-clé
set
. - Encapsule la représentation interne.
- Facile à ajouter un log des activités et la gestion des erreurs.
- Vous pouvez paresseusement charger les attributs de votre objet, disons l'obtenir à partir d'un serveur.
Mal:
type BankAccount = {
balance: number;
// ...
}
const value = 100;
const account: BankAccount = {
balance: 0,
// ...
};
if (value < 0) {
throw new Error('Cannot set negative balance.');
}
account.balance = value;
Bien:
class BankAccount {
private accountBalance: number = 0;
get balance(): number {
return this.accountBalance;
}
set balance(value: number) {
if (value < 0) {
throw new Error('Cannot set negative balance.');
}
this.accountBalance = value;
}
// ...
}
// Now `BankAccount` encapsulates the validation logic.
// If one day the specifications change, and we need extra validation rule,
// we would have to alter only the `setter` implementation,
// leaving all dependent code unchanged.
const account = new BankAccount();
account.balance = 100;
Faire en sorte que les objets aient des membres privés/protégés
TypeScript prend en charge les accesseurs public
(par défaut), protected
et private
sur les membres de la classe.
Mal:
class Circle {
radius: number;
constructor(radius: number) {
this.radius = radius;
}
perimeter() {
return 2 * Math.PI * this.radius;
}
surface() {
return Math.PI * this.radius * this.radius;
}
}
Bien:
class Circle {
constructor(private readonly radius: number) {
}
perimeter() {
return 2 * Math.PI * this.radius;
}
surface() {
return Math.PI * this.radius * this.radius;
}
}
Préférer l'immuabilité
Le système de types de TypeScript vous permet de marquer des attributs individuels
sur une interface/classe comme readonly. Cela vous permet de travailler de manière
fonctionnelle (une mutation inattendue est mauvaise). Pour les scénarios plus avancés,
il existe un type intégré Readonly
qui prend un typeT
et marque toutes ses
attributs comme étant “readonly” à l'aide de types mappés
(voir mapped types ou "types mappés").
Mal:
interface Config {
host: string;
port: string;
db: string;
}
Bien:
interface Config {
readonly host: string;
readonly port: string;
readonly db: string;
}
Dans le cas des tableaux, vous pouvez créer un tableau de readonly en utilisant
ReadonlyArray<T>
.
N'autorisez pas les modifications telles que push()
et fill()
, mais plutôt
utilisez des fonctionnalités telles que concat()
et slice()
vu que celles-ci
ne modifient pas la valeur.
Mal:
const array: number[] = [ 1, 3, 5 ];
array = []; // error
array.push(100); // array will updated
Bien:
const array: ReadonlyArray<number> = [ 1, 3, 5 ];
array = []; // error
array.push(100); // error
Déclarer des paramètres de type "read-only" dans TypeScript 3.4 est un peu plus facile.
function hoge(args: readonly string[]) {
args.push(1); // error
}
Préférer les assertions "const" pour des valeurs littérales.
Mal:
const config = {
hello: 'world'
};
config.hello = 'world'; // value is changed
const array = [ 1, 3, 5 ];
array[0] = 10; // value is changed
// writable objects is returned
function readonlyData(value: number) {
return { value };
}
const result = readonlyData(100);
result.value = 200; // value is changed
Bien:
// read-only object
const config = {
hello: 'world'
} as const;
config.hello = 'world'; // error
// read-only array
const array = [ 1, 3, 5 ] as const;
array[0] = 10; // error
// You can return read-only objects
function readonlyData(value: number) {
return { value } as const;
}
const result = readonlyData(100);
result.value = 200; // error