TypeScript: La Masturbation Intellectuelle du Dev Web ?
Publié le 15 mars 2024 - par Andy Cinquin
TypeScript vs JavaScriptDéveloppement web avancéComplexité du codeTypes statiques et dynamiquesSur-ingénierie dans le développementUtilité de TypeScript pour les projetsExemples de code TypeScriptBibliothèques UI pour le développement front-endTests unitaires et E2EAutocomplétion et IDE modernesPerformance des environnements de développementSimplification du développement webGestion des erreurs et qualité du codeTechnologies de développement moderneZod pour la validation des types
Introduction:
"Pourquoi tout le monde parle de TypeScript comme si c'était la révolution du dev web ?".
Eh bien, laisse-moi te dire que derrière le buzz, il y a beaucoup de "masturbation intellectuelle" autour de TypeScript. C'est un terme un peu brut, je sais, mais il capture l'essence de ce phénomène où on complexifie des choses simples pour le plaisir de se sentir plus intelligents. Allez, on décortique ça ensemble.
Partie pour les débutants
C'est quoi TypeScript, en fait ?
TypeScript, c'est un "superset" de JavaScript. Ça veut dire quoi ? Imagine que JavaScript est une pizza basique – fromage et tomate. TypeScript, c'est la même pizza, mais avec des tonnes d'ingrédients en plus. Sur le papier, ça a l'air génial, sauf que parfois, tu veux juste une pizza simple qui fait le job.
Pourquoi certains disent que TypeScript c'est top ?
La grande promesse de TypeScript, c'est d'ajouter des "types" à JavaScript. C'est supposé t'aider à éviter des erreurs en te forçant à définir à l'avance le genre de données (nombre, texte, etc.) que tu manipules. Ça semble utile, surtout quand tu travailles sur de gros projets.
Mais alors, c'est quoi le problème ?
Le hic, c'est que cette couche supplémentaire rajoute pas mal de complexité. Pour un projet simple ou de taille moyenne, ça peut transformer une balade tranquille en parcours du combattant.
Exemple pour les débutants
Imaginons que tu veuilles juste additionner deux nombres en JavaScript :
function add(a, b) {
return a + b
}
console.log(add(5, 3)) // Résultat: 8
Simple, non ? Maintenant, voilà à quoi ça ressemblerait en TypeScript :
function add(a: number, b: number): number {
return a + b;
}
console.log(add(5, 3)); // Résultat: 8
On doit préciser que
a
et b
sont des nombres (number
). Pour ce simple bout de code, ça va encore, mais imagine sur un projet entier !Partie pour les experts
La sur-ingénierie de TypeScript
Pour les experts parmi vous, l'attrait de TypeScript peut résider dans sa capacité à modéliser des types complexes et à fournir un système de types avancé. Cependant, cette sophistication vient avec son lot de complexité. Dans de nombreux cas, cette complexité n'apporte pas de valeur ajoutée proportionnelle au temps investi.
Les cas extrêmes où TypeScript complique plus qu'il n'aide
Prenons l'exemple de la modélisation d'une réponse d'API extrêmement hétérogène. En TypeScript, vous pourriez finir par définir des dizaines de interfaces pour gérer tous les cas possibles. Cela devient rapidement une usine à gaz, surtout si l'API change souvent.
Exemple pour les experts
Imaginons maintenant que vous devez gérer une réponse d'API complexe. En TypeScript, vous allez peut-être écrire quelque chose comme :
interface ApiResponse {
userId: number;
id: number;
title: string;
completed: boolean;
}
function fetchTodo(): Promise<ApiResponse> {
// Simulation d'un appel API
return fetch('https://example.com/todos/1').then(response => response.json());
}
C'est précis, mais regardez la version JavaScript :
async function fetchTodo() {
const response = await fetch('https://example.com/todos/1')
return await response.json()
}
En JavaScript, on va droit au but. On récupère la réponse, et on fait confiance à notre connaissance du contexte pour gérer les données correctement.
Conclusion:
Alors, TypeScript, génie ou masturbation intellectuelle ? La vérité, comme souvent, se situe quelque part au milieu. Pour les gros projets avec de nombreuses mains dans le code, TypeScript peut être un outil précieux. Mais pour beaucoup d'autres situations, il ajoute une couche de complexité qui peut ralentir et compliquer inutilement le développement.
Rappelons-nous qu'à la fin de la journée, l'objectif est de créer des applications fonctionnelles et agréables pour l'utilisateur. La simplicité a souvent sa propre forme d'élégance et d'efficacité. Et parfois, une bonne vieille pizza fromage-tomate, c'est exactement ce dont on a besoin.
L'Argument de l'Amélioration des IDE : Une Relique du Passé ?
Un argument souvent avancé en faveur de TypeScript est son impact positif sur les performances des environnements de développement intégrés (IDE) : en effet, grâce à sa rigueur typologique, il facilite l'autocomplétion et améliore potentiellement la productivité du développeur. Cependant, avec l'avènement des technologies d'intelligence artificielle et les progrès considérables réalisés dans les IDE modernes, cet avantage tend à devenir obsolète.
Les IDE actuels, armés d'algorithmes avancés et d'IA, sont capables de fournir des suggestions et autocomplétions extrêmement précises, même dans des langages dynamiques comme JavaScript. Cette évolution technologique remet en question la nécessité d'un système de typage strict comme celui proposé par TypeScript, particulièrement pour les projets qui ne justifient pas sa complexité supplémentaire.
En réalité, le gain marginal en productivité offert par les fonctionnalités d'autocomplétion de TypeScript pourrait être contrebalancé par le temps investi à définir et à maintenir des structures de types complexes. Dans l'ère de l'IA, où les outils de développement évoluent à grands pas, la simplicité et la flexibilité de JavaScript retrouvent leur place au cœur de l'efficacité du développeur.
Vers une Réconciliation des Approches
Ce n'est pas tant TypeScript en lui-même qui est en question, mais plutôt l'usage qu'on en fait. Pour des projets d'envergure, exigeant une grande maintenabilité et évolutivité, TypeScript a toute sa place. Pourtant, il est crucial de reconnaître que dans un monde technologique en constante évolution, les outils d'hier ne sont pas forcément adaptés aux défis de demain.
La clé réside dans une évaluation critique et contextuelle de l'utilité de TypeScript pour chaque projet. Comme pour notre pizza, il s'agit de choisir les ingrédients en fonction du goût souhaité et non par habitude ou sous la pression des tendances.
Rappelons-nous que l'objectif ultime est de construire des applications qui enchantent les utilisateurs, et ce chemin peut être aussi varié que les développeurs qui l'empruntent. Parfois, embrasser la simplicité et l'agilité du JavaScript natif, c'est exactement ce qu'il faut pour cuisiner quelque chose de délicieux.
Un dernier petit exemple extrême avec l'utilisation de zod par dessus pour la route !
Voici un morceau de code qui gère la génération de fichier dans une application :
en js :
import { generateData } from '@api/utilityAPI';
import { CustomModal } from '@components/Utility/UtilityModal/CustomModal';
import { Button, CircularProgress } from '@mui/material';
import { handleError } from '@utils/errors/handleCustomError';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './UtilityModule.module.scss';
const DataGeneration = ({ id }) => {
const { t } = useTranslation();
const [invalidData, setInvalidData] = useState(null);
const [isPanelOpen, setIsPanelOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const handleAction = () => {
setIsLoading(true);
generateData(id)
.then((response) => {
FileUtil.download(response.blob, response.fileName);
})
.catch(async (error) => {
let invalidDataTemp;
if (error.response.data.type === 'application/problem+json') {
const jsonError = JSON.parse(await error.response.data.text());
if (jsonError.title === 'INVALID_DATA') {
invalidDataTemp = jsonError.invalidData; // Directly using the parsed JSON
}
}
if (invalidDataTemp) {
setInvalidData(invalidDataTemp);
setIsPanelOpen(true);
} else {
handleError(error);
}
})
.finally(() => setIsLoading(false));
};
return (
<>
<Button
variant="contained"
onClick={handleAction}
disabled={isLoading}
className={styles.actionButton}
>
{isLoading && <CircularProgress size="1rem" />}
{t('utility.actionButton')}
</Button>
{invalidData && (
<CustomModal isOpen={isPanelOpen}
onClose={() => setIsPanelOpen(false)}
invalidData={invalidData}
/> )}
</>
);
};
export default DataGeneration;
Et voici en typescript :
import { generateData } from '@api/utilityAPI';
import {
AnonymizedDataSchema,
TAnonymizedDataResponse
} from '@appTypes/utility/response/AnonymizedResponse';
import { CustomModal } from '@components/Utility/UtilityModal/CustomModal';
import { Button, CircularProgress } from '@mui/material';
import { FileUtil } from '@utils/UtilityFiles';
import handleError from '@utils/errors/handleCustomError';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './UtilityModule.module.scss';
type ActionProps = {
id: number;
};
export const DataGeneration = ({ id }: ActionProps) => {
const { t } = useTranslation();
const [invalidData, setInvalidData] = useState<TAnonymizedDataResponse | null>(null);
const [isPanelOpen, setIsPanelOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const handleAction = () => {
setIsLoading(true);
generateData(id)
.then((response) => {
FileUtil.download(response.blob, response.fileName);
})
.catch(async (error) => {
let invalidDataTemp;
if (error.response.data.type === 'application/problem+json') {
const jsonError = JSON.parse(await error.response.data.text());
if (jsonError.title === 'INVALID_DATA') {
invalidDataTemp = AnonymizedDataSchema.parse(jsonError.invalidData);
}
}
if (invalidDataTemp) {
setInvalidData(invalidDataTemp);
setIsPanelOpen(true);
} else {
handleError(error);
}
})
.finally(() => setIsLoading(false));
};
return (
<>
<Button
variant="contained"
onClick={handleAction}
disabled={isLoading}
className={styles.actionButton}
>
{isLoading && <CircularProgress size="1rem" />}
{t('utility.actionButton')}
</Button>
{invalidData && (
<CustomModal isOpen={isPanelOpen}
onClose={() => setIsPanelOpen(false)}
invalidData={invalidData}
/> )}
</>
);
};
----
import { z } from 'zod';
export const AnonymizedDocumentSchema = z.object({
documentFlag: z.boolean(),
optionalIds: z.array(z.number()).optional().nullable(),
});
export const PreEvaluationDataSchema = z.object({
sourceFlag: z.boolean(),
dobFlag: z.boolean(),
birthPlaceFlag: z.boolean(),
citizenshipFlag: z.boolean(),
emailFlag: z.boolean(),
idNumberFlag: z.boolean(),
certFlag: z.boolean(),
institutionFlag: z.boolean(),
studyLevelFlag: z.boolean(),
studyAreaFlag: z.boolean(),
entryDateFlag: z.boolean(),
desiredStartDateFlag: z.boolean(),
agentNameFlag: z.boolean(),
});
export const ProposalEvaluationSchema = z.object({
proposalDateFlag: z.boolean(),
authorFullNameFlag: z.boolean(),
answerDateFlag: z.boolean(),
});
export const InterviewDataSchema = z.object({
contactFullNameFlag: z.boolean(),
meetingDateFlag: z.boolean(),
summaryFlag: z.boolean(),
prosFlag: z.boolean(),
consFlag: z.boolean(),
meetingPlaceFlag: z.boolean(),
});
// Using TypeScript to infer the types from Zod schemas
export type TAnonymizedDocument = z.infer<typeof AnonymizedDocumentSchema>;
export type TPreEvaluationData = z.infer<typeof PreEvaluationDataSchema>;
export type TProposalEvaluation = z.infer<typeof ProposalEvaluationSchema>;
export type TInterviewData = z.infer<typeof InterviewDataSchema>;
// Merging schemas for a comprehensive response structure
export const ComprehensiveEvaluationSchema = AnonymizedDocumentSchema.merge(
PreEvaluationDataSchema.merge(
ProposalEvaluationSchema.merge(
z.object({
docTypeValidityFlag: z.boolean(),
minimumInterviewsFlag: z.boolean(),
interviewFeedbackMap: z.record(z.string(), InterviewDataSchema),
})
)
)
);
export type TComprehensiveEvaluation = z.infer<typeof ComprehensiveEvaluationSchema>;
à toi de juger, quelle version est la meilleure ?
Des commentaires suffirait non ? Si nos contrats d'api sont bien contrôlés en back-end ?
En vous remerciant de votre visite, n'hésitez pas à me
contacter pour toute demande de renseignements, devis ou
proposition de collaboration. Je me ferai un plaisir de
vous répondre dans les plus brefs délais.
Vous avez aimé cet article ? N'hésitez pas à le partager !