Aller au contenu principal

Ne pas utiliser as unknown as MyClass

23 mai 2023

TL;DR

Préférer les interfaces, l'abstraction et la séparation des responsabilités.

Contributeurs

Suxue Li, Julie Brunetto, Guillaume Moizan, Gauthier Fiorentino, Dorian De Rosa

Statut

Accepté

Contexte

On veut éviter d'avoir des casts en unknown comme ça :

return {
envoyerAnalyticsPageVue: jest.fn(),
isAnalyticsAutorisé: jest.fn(() => false),
} as unknown as AnalyticsService;

Ces casts permettent de forcer le typage même lorsque l'objet ne respecte pas le contrat de la class, par exemple :

return {
envoyerAnalyticsPageVue: jest.fn(),
} as unknown as AnalyticsService;
// pas d'erreur de typage malgré le manque d'une méthode

return {
envoyerAnalyticsPageVue: jest.fn(),
isAnalyticsAutorisé: jest.fn(() => false),
truc: () => 'test'
} as unknown as AnalyticsService;
// pas d'erreur malgré la méthode superflue

return {
envoyerAnalyticsPageVue: jest.fn(),
isAnalyticsAutorisé: () => 'toto',
} as unknown as AnalyticsService;
// pas d'erreur malgré le prototype de la méthode incohérent avec son implémentation

Décision

Ne pas utiliser as unknown as MyClass.

Conséquences

  • Si la douleur vient de champs privés que typescript demande d'implémenter ==> passer par une interface n'exposant que les champs publics de la classe
class Toto {
public truc() {}
private machin() {}
}

// devient

interface Toto {
truc(): void;
}
class TotoImplementation implements Toto {
public truc() {}
private machin() {}
}
  • Si la douleur vient du fait qu'on ne se sert que d'une partie de la classe ==> Séparer la classe en plusieurs classes à responsabilités uniques
class MonService {
public doStuff1() {}
public doStuff2() {}
}

// devient

class MonServiceStuff1 {
public execute() {}
}
class MonServiceStuff2 {
public execute() {}
}
  • Si la douleur vient de l'utilisation d'une bibliothèque externe ==> Abstraire le typage avec uniquement les champs de la bibliothèque utilisés
function Truc(response: AxiosResponse<string>): void {}

// devient

type TrucResponse = { data: string }
function Truc(response: TrucResponse): void {}