TypeScript - 3/3 : Installation & Migration

typescript-logo

Suite de la deuxième partie du dossier.

Installation

npm install -g typescript  

Le paquet typescript contient le compilateur tsc découvert dans la première partie du dossier.

L'installation du paquet tslint est aussi conseillée (globale, ou locale à votre projet).

Dans le répertoire du projet, sont requis (recommandés) :

  • Un fichier tsconfig.json, généré avec la commande tsc --init ou récupéré d'un projet type boilerplate.
  • Un fichier tslint.json, généré avec la commande tslint --init ou récupéré d'un projet type boilerplate. Pour "linter" le projet, on exécute tslint -p . à la racine du projet.

    -p indique l'emplacement de tsconfig.json (détails)

  • Un éditeur de code avec un support décent de TypeScript : VSCode, WebStorm, ...


Intégration

Frontend

Il existe des configurations/plugins pour les principaux outils de transpilation et packaging:

Backend et scripts

ts-node accélère légèrement le processus de développement, en permettant à node de charger des modules TypeScript (transpilés en mémoire lors de l'import).

Il peut être utilisé en ligne de commande, pour exécuter directement des fichiers TypeScript :

ts-node index.ts  

ou bien exécuté comme module, dans un point d'entrée JavaScript classique (node index.js) :

require('ts-node').register();

// on peut ensuite importer un fichier TypeScript (./src/index.ts)
require('./src/index');  


Migration

L'intégration progressive de TypeScript dans un projet JavaScript existant est un bon exercice pour adopter le langage.

Adapter le code existant

  • Activer allowJs (tsconfig.json) afin que le code existant soit intégré au code généré par tsc.
  • Procéder à la modification du code, fichier par fichier, à l'aide de l'annotation // @ts-check.

@ts-check

tsc effectue l'analyse statique du code JavaScript contenant le commentaire // @ts-check. Il est aussi possible d'utiliser l'option --checkJs (CLI de tsc, ou dans tsconfig.json) pour analyser tous les fichiers JavaScript passés en entrée.

Si vous optez pour checkJs, utilisez @ts-nocheck pour exclure certains fichiers JavaScript de l'analyse. Pour exclure seulement certaines lignes de code, utilisez @ts-ignore (avant chaque ligne).

Mieux encore : TypeScript peut aussi utiliser votre JSDoc pour trouver des erreurs dans votre code.

Voici un exemple, normalize-name.js :

// @ts-check

/**
 * @param {string} name Name to be normalized
 *
 * @return {string} Normalized name.
 */
function normalizeName(name) {  
  return name.trim().toUpperCase();
}

// Erreur : 5 n'est pas une string
normalizeName(5);  
// Erreur : normalizeName retourne une string (calcul impossible)
const a = 3 * normalizeName('MiKe');  

À la compilation tsc renvoie les erreurs suivantes :

normalize-name.js(13,15): error TS2345: Argument of type '5' is not assignable to parameter of type 'string'.

normalize-name.js(15,15): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.

Par contre, dans le code code ci-dessus, passer undefined à normalizeName n'est pas signalé comme une erreur par tsc. En effet, il s'agit de JavaScript, non de TypeScript. Il en va de même avec TypeScript, si l'option strict n'est pas activée : tsc transpile l'instruction sans nous avertir.

normalizeName(undefined);  

Cette instruction provoquera l'erreur TypeError: Cannot read property 'trim' of undefined à l'exécution. Une erreur de runtime facilement évitable avec TypeScript et le mode strict.

Changement de l'extension "js" en "ts"

En effet, avec l'option "strict" à true dans tsconfig.json, on obtient l'erreur suivante en tentant de transpiler ce code TypeScript (normalize-name.ts, notez l'extension .ts) :

normalize-name.ts(13,15): error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'

Pour un code TypeScript (.ts, .tsx), tsc tient compte de la configuration établie dans tsconfig.json.

Par conséquent, l'adoption progressive (fichier par fichier) peut se faire en deux temps. D'abord à l'aide de @ts-check; ensuite en changeant l'extension js du fichier en ts.

L'option sourceMap permet de générer les source maps correspondant au code transpilé.


Packages NPM et Imports

Pour les packages NPM importés dans le projet, tsc s'attend à ce qu'ils possèdent un fichier de déclaration. Certains packages NPM en sont déjà équipés : c'est le cas idéal. Sinon il reste deux possibilités :

  • Installer les packages DefinitelyTyped correspondants. Il en existe pour la plupart des packages (@types/node, @types/express, @types/lodash, @types/jest, ...).
  • Créer des déclarations vides (stubs) de ces packages, pour autoriser leur import sans qu'ils soient signalés comme inconnus ("Could not find a declaration file for module '...' [...]") :
declare module 'nom-du-module';  

C'est une solution de "secours" lorsqu'aucun package @types correspondant n'est disponible, ou qu'il n'est pas à jour. Vous pouvez aussi définir vous-même la déclaration du package et la partager avec la communauté.

Pour permettre l'import de fichiers JSON (import * as fileContent from './file.json') :

declare module '*.json' {  
  const value: any;
  export default value;
}

Pour générer les fichiers de déclaration de votre projet (par exemple, si c'est un projet open-source), activez l'option declaration.


Conclusion

Vous trouverez d'avantage de détails sur la migration depuis JavaScript dans la documentation officielle.

Une fois n'est pas coutume, j'insiste sur l'importance de l'option strict. Comme le précise la documentation, elle entraîne l'activation de plusieurs règles (dont noImplicitAny ) qui rendent tsc très exigeant sur la qualité (et le typage) de votre code mais permettent de tirer le meilleur profit de TypeScript.