AssetMapper et TypeScript : récupérer les types manquants avec un bundle Symfony
AssetMapper simplifie la gestion des assets dans Symfony, mais il ne télécharge pas les types TypeScript. Votre IDE ne connaît rien des libs JS que vous utilisez, tout est any implicite, et les erreurs de type passent à la trappe.
Le problème : AssetMapper ne télécharge pas les types
Avec npm, quand on installe une dépendance, on récupère l'intégralité du package : les sources, les fichiers de déclaration TypeScript (.d.ts), les métadonnées. TypeScript sait où chercher les types, et l'IDE suit.
AssetMapper fonctionne différemment. Il télécharge uniquement le fichier JS exposé par le package, ce qui suffit pour l'exécution, mais pas pour le type checking. Les fichiers .d.ts ne font pas partie du bundle téléchargé.
Conséquence directe : TypeScript et les IDE comme VSCode ou PhpStorm ne trouvent aucune définition de type pour tes imports. Tout est any implicite. Les erreurs de type ne sont pas détectées et l'autocomplétion ne fonctionne pas.
Comment TypeScript résout les types
Avant de chercher une solution, il faut comprendre le mécanisme de résolution.
Un package JS peut embarquer ses propres définitions de types via un champ types ou typings dans son package.json. Ce champ pointe vers un fichier .d.ts racine, qui peut lui-même importer d'autres fichiers de déclaration. C'est ce graphe de fichiers que TypeScript parcourt pour résoudre les types à la compilation.
Dans un projet standard avec npm, tout ça est dans node_modules. TypeScript sait regarder là par défaut. Sans node_modules, il faut lui dire explicitement où chercher. C'est le rôle de la config paths dans tsconfig.json.
{ "compilerOptions": { "paths": { "stimulus": ["./assets/vendor/@hotwired/stimulus/types/index.d.ts"] } } }
pathsfait correspondre un identifiant d'import (commestimulus) à un ou plusieurs chemins de fichiers locaux. TypeScript les parcourt dans l'ordre jusqu'à trouver une correspondance.
La solution manuelle
La démarche est simple, mais répétitive à faire à la main pour chaque package.
Première étape : identifier si le package expose des types. On consulte son package.json sur jsDelivr ou npm, et on cherche le champ types ou typings.
curl https://cdn.jsdelivr.net/npm/@hotwired/stimulus/package.json | jq '.types' # → "dist/types/index.d.ts"
Deuxième étape : récupérer le fichier .d.ts racine, puis chaque fichier importé récursivement. Un fichier de déclaration peut en importer d'autres via des chemins relatifs — il faut tous les télécharger pour que la résolution fonctionne.
# Récupérer le fichier racine curl -o assets/vendor/@hotwired/stimulus/dist/types/index.d.ts \ https://cdn.jsdelivr.net/npm/@hotwired/stimulus/dist/types/index.d.ts # Récupérer les fichiers référencés curl -o assets/vendor/@hotwired/stimulus/dist/types/core.d.ts \ https://cdn.jsdelivr.net/npm/@hotwired/stimulus/dist/types/core.d.ts
Troisième étape : mettre à jour le tsconfig.json avec l'entrée paths correspondante.
Cette procédure fonctionne. On l'a validée sur plusieurs packages. Mais elle ne passe pas à l'échelle : à chaque importmap:require, tu recommences. Et si un package met à jour ses types, tu ne le sais pas.
TypescriptTypesBundle : automatiser tout ça
Pour éviter de reproduire cette procédure à la main, on a développé IQ2i/typescript-types-bundle. L'idée est simple : lire importmap.php, détecter les packages qui exposent des types, télécharger les fichiers .d.ts, et mettre à jour tsconfig.json.
L'installation se fait via Composer :
composer require iq2i/typescript-types-bundle
Une fois installé, une seule commande suffit pour synchroniser les types de tous les packages déclarés dans l'importmap :
php bin/console typescript-types:sync
Le bundle interroge jsDelivr pour récupérer le package.json de chaque package, vérifie la présence d'un champ types ou typings, télécharge récursivement les fichiers .d.ts référencés, puis met à jour tsconfig.json. Voici ce que ça donne sur un projet avec Stimulus et Tom Select :
{ "compilerOptions": { "paths": { "@hotwired/stimulus": [ "assets/vendor/@hotwired/stimulus/dist/types/index.d.ts" ], "tom-select": [ "assets/vendor/tom-select/dist/types/tom-select.d.ts" ] } } }
Le bundle ne touche que la section
paths. Le reste de tontsconfig.jsonest préservé. Si un package ne déclare pas de champtypesdans sonpackage.json, il est simplement ignoré sans erreur.
Essayez le dès maintenant !
Le bundle est open source et disponible sur GitHub : IQ2i/typescript-types-bundle. Les contributions sont les bienvenues, notamment pour les cas limites : packages qui externalisent leurs types dans un package @types/* séparé, ou packages sans package.json accessible via jsDelivr.