Définition de TypeScript pour les projets Modern React à l'aide de Webpack et Babel
À l'ère du développement logiciel, JavaScript peut être utilisé pour développer presque n'importe quel type d'application. Cependant, le fait que JavaScript soit typé dynamiquement pourrait être une préoccupation pour la plupart des grandes entreprises, en raison de sa fonction de vérification de type lâche.
Heureusement, nous n'avons pas à attendre le Comité technique Ecma 39 introduit un système de type statique dans JavaScript. Nous pouvons plutôt utiliser TypeScript.
JavaScript, étant typé dynamiquement, ne connaît pas le type de données d'une variable tant que cette variable n'est pas instanciée au moment de l'exécution. Les développeurs qui écrivent de gros logiciels peuvent avoir tendance à réaffecter une variable, déclarée plus tôt, à une valeur d'un type différent, sans avertissement ni problème, ce qui entraîne des bogues souvent ignorés.
Dans ce didacticiel, nous allons apprendre ce que TypeScript est et comment travailler avec lui dans un projet React. À la fin, nous aurons construit un projet consistant en une application de sélection d'épisode pour l'émission de télévision Money Heist en utilisant TypeScript et les crochets actuels de type React ( useState
useEffect
useReducer
useContext
). Avec ces connaissances, vous pouvez continuer à expérimenter avec TypeScript dans vos propres projets.
Cet article n'est pas une introduction à TypeScript . Par conséquent, nous ne passerons pas par la syntaxe de base de TypeScript et JavaScript. Cependant, vous n'avez pas besoin d'être un expert dans l'un de ces langages pour suivre, car nous allons essayer de suivre le principe KISS (garder les choses simples, stupides).
Qu'est-ce que TypeScript?
En 2019 , TypeScript a été classé septième langage le plus utilisé et cinquième langage à la croissance la plus rapide sur GitHub. Mais qu'est-ce que TypeScript exactement?
Selon la documentation officielle TypeScript est un sur-ensemble typé de JavaScript qui se compile en JavaScript simple. Il est développé et maintenu par Microsoft et la communauté open-source.
«Superset» dans ce contexte signifie que le langage contient toutes les caractéristiques et fonctionnalités de JavaScript, puis certaines. TypeScript est un langage de script typé.
Il offre aux développeurs plus de contrôle sur leur base de code via son annotation de type, ses classes et son interface, évitant ainsi aux développeurs d'avoir à corriger manuellement des bogues gênants dans la console.
TypeScript n'a pas été créé pour modifier JavaScript. Au lieu de cela, il se développe sur JavaScript avec de nouvelles fonctionnalités précieuses. Tout programme écrit en JavaScript simple s'exécutera également comme prévu dans TypeScript, y compris les applications mobiles multiplateformes et les backends dans Node.js.
Cela signifie que vous pouvez également écrire des applications React en TypeScript, comme nous le ferons dans ce didacticiel.
Pourquoi TypeScript?
Peut-être, vous n'êtes pas convaincu d'embrasser la bonté de TypeScript. Examinons quelques-uns de ses avantages.
Moins de bogues
Nous ne pouvons pas éliminer tous les bogues de notre code, mais nous pouvons les réduire. TypeScript vérifie les types au moment de la compilation et génère des erreurs si le type de variable change.
Le fait de pouvoir détecter ces erreurs évidentes mais fréquentes dès le début facilite beaucoup la gestion de votre code avec les types.
La refactorisation est plus facile
Vous voudrez probablement refactoriser pas mal de choses, mais parce qu'elles touchent tellement d'autres codes et de nombreux autres fichiers, vous avez peur de les modifier.
Dans TypeScript, de telles choses peuvent souvent être refactorisées avec juste un clic sur la commande " Renommer le symbole " dans votre environnement de développement intégré (IDE).

Dans un langage typé dynamiquement tel en tant que JavaScript, le seul moyen de refactoriser plusieurs fichiers en même temps est d'utiliser la fonction traditionnelle de «recherche et remplacement» à l'aide d'expressions régulières (RegExp).
Dans un langage de type statique tel que TypeScript, «rechercher et remplacer» n'est pas » t plus nécessaire. Avec les commandes IDE telles que «Rechercher toutes les occurrences» et «Renommer le symbole», vous pouvez voir toutes les occurrences dans l'application de la fonction, de la classe ou de la propriété d'une interface d'objet.
TypeScript vous aidera à trouver toutes les instances de la refactorisé, renommez-le et vous alertera avec une erreur de compilation au cas où votre code aurait des incompatibilités de type après la refactorisation.
TypeScript a même plus d'avantages que ce que nous avons couvert ici.
Inconvénients de TypeScript
TypeScript n'est certainement pas sans inconvénients, même compte tenu des fonctionnalités prometteuses soulignées ci-dessus.
Un faux sens de la sécurité
La fonction de vérification de type de TypeScript crée souvent un faux sentiment de sécurité chez les développeurs. La vérification de type nous avertit en effet lorsque quelque chose ne va pas avec notre code. Cependant, les types statiques ne réduisent pas la densité globale des bogues.
Par conséquent, la force de votre programme dépendra de votre utilisation de TypeScript, car les types sont écrits par le développeur et non vérifiés au moment de l'exécution.
Si vous êtes Si vous cherchez à TypeScript pour réduire vos bogues, veuillez plutôt envisager un développement piloté par les tests .
Système de typage compliqué
Le système de typage, bien qu'il soit un excellent outil à bien des égards, peut parfois être un peu compliqué. Cet inconvénient provient du fait qu'il est entièrement interopérable avec JavaScript, ce qui laisse encore plus de place à la complication.
Cependant, TypeScript est toujours JavaScript, donc la compréhension de JavaScript est importante .
Quand utiliser TypeScript? [19659019] Je vous conseille d'utiliser TypeScript dans les cas suivants:- Si vous souhaitez créer une application qui sera maintenue sur une longue période je vous recommande fortement de commencer par TypeScript, car il favorise le code auto-documenté, aidant ainsi les autres développeurs à comprendre facilement votre code lorsqu'ils rejoignent votre base de code.
- Si vous devez créer une bibliothèque pensez à l'écrire en TypeScript. Il aidera les éditeurs de code à suggérer les types appropriés aux développeurs qui utilisent votre bibliothèque.
Dans les dernières sections, nous avons équilibré les avantages et les inconvénients de TypeScript. Passons à la tâche courante: configuration de TypeScript dans un projet React moderne .
Mise en route
Il existe plusieurs façons de configurer TypeScript dans un projet React. Dans ce didacticiel, nous n'en couvrirons que deux.
Méthode 1: Créer React App + TypeScript
Il y a environ deux ans, l'équipe React a publié Create React App 2.1, avec Prise en charge de TypeScript . Ainsi, vous n'aurez peut-être jamais à faire de gros efforts pour intégrer TypeScript dans votre projet.

Pour démarrer un nouveau projet Create React App, vous pouvez l'exécuter…
npx create-react-app my-app --folder-name
… ou ceci:
fil crée react-app my-app --folder-name
Pour ajouter TypeScript à un projet Create React App, installez-le d'abord et ses @types respectifs
:
npm install --save typescript @ types / node @ types / react @ types / react-dom @ types / jest
… ou:
fil ajouter typescript @ types / node @ types / react @ types / react-dom @ types / jest
Ensuite, renommez les fichiers (par exemple, index.js
en index.tsx
) et redémarrez votre serveur de développement !
Méthode 2: Configurer TypeScript avec Webpack
Webpack est un bundle de modules statiques pour les applications JavaScript. Il prend tout le code de votre application et le rend utilisable dans un navigateur Web. Les modules sont des morceaux de code réutilisables construits à partir du JavaScript de votre application, node_modules
des images et des styles CSS, qui sont conçus pour être facilement utilisés sur votre site Web.
Créer un nouveau projet
Commençons par création d'un nouveau répertoire pour notre projet:
mkdir react-webpack
cd react-webpack
Nous utiliserons npm pour initialiser notre projet:
npm init -y
La commande ci-dessus va générer un fichier package.json
avec quelques valeurs par défaut. Ajoutons également quelques dépendances pour le webpack, TypeScript et certains modules spécifiques à React.
Installation des packages
Enfin, nous devons installer les packages nécessaires. Ouvrez votre interface de ligne de commande (CLI) et exécutez ceci:
#Installing devDependencies
npm install --save-dev @ types / react @ types / react-dom awesome-typescript-loader css-loader html-webpack-plugin mini-css-extract-plugin source-map-loader typescript webpack webpack-cli webpack-dev -serveur
#installing Dependencies
npm installer react react-dom
Ajoutons également manuellement quelques fichiers et dossiers différents dans notre dossier react-webpack
:
- Ajoutez
webpack.config.js
pour ajouter des configurations liées aux webpack. [19659041] Ajouteztsconfig.json
pour toutes nos configurations TypeScript. - Ajoutez un nouveau répertoire,
src
. - Créez un nouveau répertoire,
composants
dans le dossiersrc
. - Enfin, ajoutez
index.html
App.tsx
etindex.tsx
dans lecomposants
dossier.
Structure du projet
Ainsi, notre structure de dossiers ressemblera à ceci:
├── package.json
├── package-lock.json
├── tsconfig.json
├── webpack.config.js
├── .gitignore
└── src
└──composants
├── App.tsx
├── index.tsx
├── index.html
Commencez à ajouter du code
Commençons par index.html
:
Configuration de React-Webpack
Cela créera le code HTML, avec une div vide
avec un ID de sortie
.
Ajoutons le code à notre composant React App.tsx
]:
importez * en tant que React de "react";
interface d'exportation HelloWorldProps {
userName: chaîne;
lang: string;
}
export const App = (accessoires: HelloWorldProps) => (
Salut {props.userName} de React! Bienvenue sur {props.lang}!
);
Nous avons créé un objet d'interface et l'avons nommé HelloWorldProps
avec userName
et lang
ayant une chaîne
de type . [19659005] Nous avons transmis les accessoires
à notre composant App
et l'avons exporté.
Maintenant, mettons à jour le code dans index.tsx
:
import * as Réagir de "réagir";
importer * comme ReactDOM à partir de "react-dom";
importer {App} à partir de "./App";
ReactDOM.render (
,
document.getElementById ("sortie")
);
Nous venons d'importer le composant App
dans index.tsx
. Lorsque webpack voit un fichier avec l'extension .ts
ou .tsx
il transpile ce fichier à l'aide de la bibliothèque awesome-typescript-loader .
TypeScript Configuration
Nous ajouterons ensuite une configuration à tsconfig.json
:
{
"options de compilation": {
"jsx": "réagir",
"module": "commonjs",
"noImplicitAny": vrai,
"outDir": "./build/",
"preserveConstEnums": vrai,
"removeComments": vrai,
"sourceMap": vrai,
"cible": "es5"
},
"inclure": [
"src/components/index.tsx"
]
}
Examinons également les différentes options que nous avons ajoutées à tsconfig.json
:
-
compilerOptions
Représente les différentes options du compilateur. -
jsx: react
Ajoute la prise en charge de JSX dans les fichiers.tsx
. -
lib
Ajoute une liste de fichiers de bibliothèque à la compilation (par exemple, l'utilisation dees2015
nous permet d'utiliser la syntaxe ECMAScript 6). -
module
Génère le code du module. -
noImplicitAny
Génère des erreurs pour les déclarations avec un typeimplicite de tout type
. -
outDir
Représente le répertoire de sortie. -
sourceMap
Génère un fichier.map
qui peut être très utile pour déboguer l'application. -
target
Représente la version ECMAScript cible vers laquelle transpiler notre code (nous pouvons ajouter une version en fonction des exigences spécifiques de notre navigateur). -
incluent
Utilisé pour spécifier la liste de fichiers à inclure.
Configuration de Webpack
Ajoutons une configuration de Webpack à webpack.config.js
.
const path = require ("path");
const HtmlWebpackPlugin = require ("html-webpack-plugin");
const MiniCssExtractPlugin = require ("mini-css-extract-plugin");
module.exports = {
entrée: "./src/components/index.tsx",
cible: "web",
mode: "développement",
production: {
path: path.resolve ( __ dirname, "build"),
nom de fichier: "bundle.js",
},
résoudre: {
extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
},
module: {
règles: [
{
test: /.(ts|tsx)$/,
loader: "awesome-typescript-loader",
},
{
enforce: "pre",
test: /.js$/,
loader: "source-map-loader",
},
{
test: /.css$/,
loader: "css-loader",
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "components", "index.html"),
}),
new MiniCssExtractPlugin({
filename: "./src/yourfile.css",
}),
],
};
Voyons les différentes options que nous avons ajoutées à webpack.config.js
:
-
entrée
Cela spécifie le point d'entrée de notre application. Il peut s'agir d'un seul fichier ou d'un tableau de fichiers que nous souhaitons inclure dans notre build. -
output
Il contient la configuration de sortie. L'application examine cela lors de la tentative de sortie du code groupé de notre projet sur le disque. Le chemin d'accès représente le répertoire de sortie pour le code à sortir, et le nom de fichier représente le nom de fichier pour le même. Il est généralement nommébundle.js
. -
résoudre
Webpack examine cet attribut pour décider de regrouper ou d'ignorer le fichier. Ainsi, dans notre projet, webpack considérera les fichiers avec les extensions.js
.jsx
.json
.ts
et.tsx
pour le regroupement. -
module
Nous pouvons permettre à webpack de charger un fichier particulier à la demande de l'application, à l'aide de chargeurs. Il prend un objet de règles qui spécifie que:- tout fichier se terminant par l'extension
.tsx
ou.ts
doit utiliserawesome-typescript-loader
pour être chargés; - les fichiers se terminant par l'extension
.js
doivent être chargés avecsource-map-loader
; - les fichiers se terminant par
.css
l'extension doit être chargée aveccss-loader
.
- tout fichier se terminant par l'extension
- plugins
Webpack a ses propres limites, et il fournit des plugins pour les surmonter et étendre ses capacités. Par exemple,html-webpack-plugin
crée un fichier de modèle rendu au navigateur à partir du fichierindex.html
dans le fichier./ src / component / index.html
.
MiniCssExtractPlugin
affiche le fichier parent CSS
du fichier de l'application.
Ajout de scripts à package.json
Nous pouvons ajouter différents scripts pour construire React applications dans notre package.json
fichier:
"scripts": {
"start": "webpack-dev-server --open",
"build": "webpack"
},
Maintenant, exécutez npm start
dans votre CLI. Si tout s'est bien passé, vous devriez voir ceci:

Si vous avez un talent pour webpack, clonez le référentiel pour cette configuration et utilisez-la dans vos projets.
Création de fichiers
Créez un dossier src
et un fichier index.tsx
. Ce sera le fichier de base qui rend React.
Maintenant, si nous exécutons npm start
il exécutera notre serveur et ouvrira un nouvel onglet. L'exécution de npm run build
créera un webpack pour la production et créera un dossier de build pour nous.
Nous avons vu comment configurer TypeScript à partir de zéro à l'aide de l'application Create React et de la méthode de configuration du webpack.
L'une des façons les plus rapides d'obtenir une compréhension complète de TypeScript consiste à convertir l'un de vos projets Vanilla React existants en TypeScript. Malheureusement, l'adoption incrémentielle de TypeScript dans un projet Vanilla React existant est stressante car elle implique d'avoir à éjecter ou renommer tous les fichiers, ce qui entraînerait des conflits et une gigantesque demande de pull si le projet appartenait à une grande équipe.
Ensuite, nous verrons comment migrer facilement un projet React vers TypeScript.
Migrer une application Create React existante vers TypeScript
Pour rendre ce processus plus facile à gérer, nous le décomposerons en étapes, ce qui nous permettra de migrer en morceaux individuels. Voici les étapes à suivre pour migrer notre projet:
- Ajouter TypeScript et types.
- Ajouter
tsconfig.json
. - Commencer petit.
- Renommer l'extension des fichiers en
.tsx
.
1. Ajouter TypeScript au projet
Tout d'abord, nous devons ajouter TypeScript à notre projet. En supposant que votre projet React a été démarré avec Create React App, nous pouvons exécuter ce qui suit:
# Utilisation de npm
npm install --save typescript @ types / node @ types / react @ types / react-dom @ types / jest
# Utilisation de fil
fil ajouter typescript @ types / node @ types / react @ types / react-dom @ types / jest
Notez que nous n'avons encore rien changé en TypeScript. Si nous exécutons la commande pour démarrer le projet localement ( npm start
ou yarn start
), rien ne change. Si tel est le cas, tant mieux! Nous sommes prêts pour la prochaine étape.
2. Ajouter le fichier tsconfig.json
Avant de tirer parti de TypeScript, nous devons le configurer via le fichier tsconfig.json
. La façon la plus simple de commencer est d'échafauder un en utilisant cette commande:
npx tsc --init
Cela nous donne quelques notions de base, avec beaucoup de code commenté. Maintenant, remplacez tout le code dans tsconfig.json
par ceci:
{
"options de compilation": {
"jsx": "réagir",
"module": "commonjs",
"noImplicitAny": vrai,
"outDir": "./build/",
"preserveConstEnums": vrai,
"removeComments": vrai,
"sourceMap": vrai,
"cible": "es5"
},
"inclure": [
"./src/**/**/*"
]
}
Configuration TypeScript
Examinons également les différentes options que nous avons ajoutées à tsconfig.json
:
-
compilerOptions
Représente les différentes options du compilateur.-
target
Traduit les nouvelles constructions JavaScript vers une version plus ancienne, comme ECMAScript 5. -
lib
Ajoute une liste de fichiers de bibliothèque à la compilation (par exemple, l'utilisation de es2015 nous permet d'utiliser la syntaxe ECMAScript 6.) -
jsx: react
Ajoute la prise en charge de JSX dans les fichiers.tsx
. -
lib
Ajoute une liste de fichiers de bibliothèque à la compilation (par exemple, l'utilisation de es2015 nous permet d'utiliser la syntaxe ECMAScript 6.) -
module
Génère le code du module. -
noImplicitAny
Utilisé pour générer des erreurs pour les déclarations avec un type implicitede tout type
. -
outDir
Représente le répertoire de sortie. -
sourceMap
Génère un fichier.map
qui peut être très utile pour déboguer notre application. -
incluent
Permet de spécifier la liste de fichiers à inclure.
-
Les options de configuration varient en fonction de la demande d'un projet. Vous devrez peut-être vérifier la feuille de calcul des options TypeScript pour déterminer ce qui conviendrait à votre projet.
Nous n'avons pris que les mesures requises pour préparer les choses. Notre prochaine étape consiste à migrer un fichier vers TypeScript.
3. Commencez avec un composant simple
Tirez parti de la capacité de TypeScript à être progressivement adopté. Allez un fichier à la fois à votre rythme. Faites ce qui a du sens pour vous et votre équipe. N'essayez pas de vous attaquer à tout cela en même temps.
Pour convertir cela correctement, nous devons faire deux choses:
- Changer l'extension du fichier en
.tsx
. - Ajouter l'annotation de type (qui nécessiterait une certaine connaissances TypeScript ).
4.Renommer les extensions de fichier à .tsx
Dans une grande base de code, il peut sembler fatigant de renommer les fichiers individuellement.
Renommer plusieurs fichiers sur macOS
Renommer plusieurs fichiers peut être une perte de temps. Voici comment vous pouvez le faire sur un Mac. Faites un clic droit (ou Ctrl
+ clic, ou cliquez avec deux doigts simultanément sur le trackpad si vous utilisez un MacBook) sur le dossier qui contient les fichiers que vous souhaitez renommer. Cliquez ensuite sur «Révéler dans le Finder». Dans le Finder, sélectionnez tous les fichiers que vous souhaitez renommer. Cliquez avec le bouton droit sur les fichiers sélectionnés et choisissez «Renommer les éléments X…». Ensuite, vous verrez quelque chose comme ceci:

Insérez la chaîne souhaitée pour trouver, et la chaîne avec laquelle vous souhaitez remplacer cette chaîne trouvée, et appuyez sur "Renommer".
Renommer plusieurs fichiers sous Windows
Renommer plusieurs fichiers sous Windows dépasse le cadre de ce didacticiel, mais un guide complet est disponible.
Vous obtiendrez généralement des erreurs après avoir renommé les fichiers; il vous suffit d'ajouter les annotations de type. Vous pouvez rafraîchir ce point dans la documentation .
Nous avons expliqué comment configurer TypeScript dans une application React. Maintenant, créons une application de sélection d'épisode pour Money Heist en utilisant TypeScript.
Nous ne couvrirons pas les types de base de TypeScript. Il est nécessaire de parcourir la documentation avant de continuer dans ce didacticiel.
Temps de construction
Pour rendre ce processus moins intimidant, nous allons le décomposer en étapes, ce qui nous permettra de construire l'application en morceaux individuels. Voici toutes les étapes que nous prendrons pour créer le sélecteur d'épisode Money Heist :
- Échafaudez une application Create React.
- Récupérez les épisodes.
- Créez les types appropriés. et interfaces pour nos épisodes dans
interface.ts
. - Configurer le magasin pour la récupération des épisodes dans
store.tsx
. - Créer l'action pour récupérer les épisodes dans
action.ts
. - Créez un composant
EpisodeList.tsx
qui contient les épisodes récupérés. - Importez le composant
EpisodesList
dans notre page d'accueil à l'aide de] React Lazy and Suspense
.
- Créez les types appropriés. et interfaces pour nos épisodes dans
- Ajouter des épisodes.
- Configurer le magasin pour ajouter des épisodes dans
store.tsx
. - Créer l'action pour ajouter des épisodes dans
action.ts
.
- Configurer le magasin pour ajouter des épisodes dans
- Supprimer les épisodes.
- Configurer le magasin pour la suppression des épisodes dans
store.tsx
. [19659041] Créez l'action de suppression des épisodes dansaction.ts
.
- Configurer le magasin pour la suppression des épisodes dans
- Épisode préféré.
- Importez
EpisodesList
composant de l'épisode préféré. - Rendu
Liste des épisodes
dans l'épisode préféré.
- Importez
- Utilisation de Reach Router pour la navigation.
Configurer React
La manière la plus simple de configurer React consiste à utiliser Créer React App . Créer une application React est un moyen officiellement pris en charge pour créer des applications React d'une seule page. Il offre une configuration de construction moderne sans configuration.
Nous nous en servirons pour amorcer l'application que nous allons créer. Depuis votre CLI, exécutez la commande ci-dessous:
npx create-react-app react-ts-app && cd react-ts-app
Une fois l'installation terminée, démarrez le serveur React en exécutant npm start
.

Comprendre les interfaces et les types en tapuscrit [19659019] Les interfaces en TypeScript sont utilisées lorsque nous devons donner des types aux propriétés des objets. Par conséquent, nous utiliserions des interfaces pour définir nos types. interface Employee {
nom: chaîne,
rôle: chaîne
salaire: nombre
}
const bestEmployee: Employee = {
nom: «John Doe»,
rôle: 'Développeur IOS',
salaire: '8500 $' // remarquez que nous utilisons une chaîne
}
interface Employee {
nom: chaîne,
rôle: chaîne
salaire: nombre
}
const bestEmployee: Employee = {
nom: «John Doe»,
rôle: 'Développeur IOS',
salaire: '8500 $' // remarquez que nous utilisons une chaîne
}
Lors de la compilation du code ci-dessus, nous verrions cette erreur: «Les types de propriété salaire
sont incompatibles. La chaîne de type
ne peut pas être affectée au numéro de type
. »
De telles erreurs se produisent dans TypeScript lorsqu'une propriété ou une variable se voit affecter un type autre que le type défini. Plus précisément, l'extrait ci-dessus signifie que la propriété salaire
a été affectée à un type de chaîne
au lieu d'un type numéro
.
Créons une interface . ts
dans notre dossier src
. Copiez et collez ce code dedans:
/ **
| ------------------------------------------------- -
| Toutes les interfaces!
| ------------------------------------------------- -
* /
interface d'exportation IEpisode {
airdate: string
piste d'atterrissage: chaîne
temps d'antenne: chaîne
id: nombre
image: {medium: string; original: string}
nom: chaîne
nombre: nombre
runtime: nombre
saison: nombre
résumé: chaîne
url: chaîne
}
interface d'exportation IState {
épisodes: Array
favoris: Array
}
interface d'exportation IAction {
type: chaîne
charge utile: Array | tout
}
type d'exportation Dispatch = React.Dispatch
type d'exportation FavAction = (
état: IState,
expédition: Dispatch,
épisode: IEpisode
) => IAction
interface d'exportation IEpisodeProps {
épisodes: Array
store: {état: IState; dispatch: Dispatch}
toggleFavAction: FavAction
favoris: Array
}
interface d'exportation IProps {
épisodes: Array
store: {état: IState; dispatch: Dispatch}
toggleFavAction: FavAction
favoris: Array
}
C'est une bonne pratique d'ajouter un «I» au nom de l'interface. Cela rend le code lisible. Cependant, vous pouvez décider de l'exclure.
Interface IEpisode
Notre API renvoie un ensemble de propriétés telles que airdate
airstamp
] airtime
id
image
nom
numéro
runtime
saison
résumé
et url
. Par conséquent, nous avons défini une interface IEpisode
et défini les types de données appropriés sur les propriétés de l'objet.
Interface IState
Notre interface IState
a ] épisodes
et propriétés favorites
respectivement, et une interface Array
.
IAction
L'interface IAction
interface les propriétés sont de type charge utile
et
. La propriété type
a un type chaîne, tandis que la charge utile a un type Array |
.
Notez que Array | any
signifie un tableau de l'interface d'épisode ou de n'importe quel type.
Le type Dispatch
est défini sur React.Dispatch
et une interface
. Notez que React.Dispatch
est le type standard pour la fonction dispatch
selon la base de code @ types / react
tandis que
est un tableau de l'action Interface.
De plus, Visual Studio Code possède un vérificateur TypeScript. Donc, en mettant simplement en surbrillance ou en survolant le code, il est assez intelligent pour suggérer le type approprié.
En d'autres termes, pour que nous puissions utiliser notre interface dans toutes nos applications, nous devons l'exporter. Jusqu'à présent, nous avons notre magasin et nos interfaces qui contiennent le type de notre objet. Créons maintenant notre magasin.
Notez que les autres interfaces suivent les mêmes conventions que celles expliquées.
Récupération des épisodes
Création d'un magasin
Pour récupérer nos épisodes, nous avons besoin d'un magasin qui contient l'état initial du
Nous utiliserons le crochet useReducer
pour configurer cela. Créez un fichier store.tsx
dans votre dossier src
. Copiez et collez-y le code suivant.
import React, {useReducer, createContext} de 'react'
importer {IState, IAction} depuis './types/interfaces'
const initialState: IState = {
épisodes: [],
favoris: []
}
export const Store = createContext (initialState)
const reducer = (état: IState, action: IAction): IState => {
commutateur (action.type) {
cas 'FETCH_DATA':
retourner {... état, épisodes: action.payload}
défaut:
état de retour
}
}
export const StoreProvider = ({children}: JSX.ElementChildrenAttribute): JSX.Element => {
const [state, dispatch] = useReducer (réducteur, initialState)
retour {enfants}
}
Voici les étapes que nous avons suivies pour créer le magasin:
- Pour définir notre magasin, nous avons besoin du
[useReducer] (https://reactjs.org/docs/hooks-reference.html #usereducer)
hook et l'APIcreateContext
de React, c'est pourquoi nous l'avons importé. - Nous avons importé
IState
etIAction
de] ./ types / interfaces
. - Nous avons déclaré un objet
initialState
avec un type deIState
et les propriétés des épisodes et des favoris, qui sont toutes les deux définies comme vides. - Ensuite, nous avons créé une variable
Store
qui contient la méthodecreateContext
et qui passe lainitialState
.
La La ] Le type de méthode createContext est
ce qui signifie qu'il peut s'agir d'un type de
ou de
. We will see the any
type used often in this article.
- Next, we declared a
reducer
function and passed instate
andaction
as parameters. Thereducer
function has a switch statement that checks the value ofaction.type
. If the value isFETCH_DATA
then it returns an object that has a copy of our state(...state)
and of the episode state that holds our action payload. - In the switch statement, we return a state of
default
.
Note that the state
and action
parameters in the reducer function have IState
and IAction
types, respectively. Also, the reducer
function has a type of IState
.
- Lastly, we declared a
StoreProvider
function. This will give all components in our app access to the store. - This function takes
children
as a prop, and inside theStorePrivder
function, we declared theuseReducer
hook. - We destructured
state
anddispatch
. - In order to make our store accessible to all components, we passed in an object value containing
state
anddispatch
.
The state
that contains our episodes and favorites state will be made accessible by other components, while the dispatch
is a function that changes the state.
- We will export
Store
andStoreProvider
so that it can be used across our application.
Create Action.ts
We’ll need to make requests to the API to fetch the episodes that will be shown the user. This will be done in an action file. Create an Action.ts
file, and then paste the following code:
import { Dispatch } from './interface/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => {
const URL =
'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes'
const data = await fetch(URL)
const dataJSON = await data.json()
return dispatch({
type: 'FETCH_DATA',
payload: dataJSON._embedded.episodes
})
}
First, we need to import our interfaces so that they can be used in this file. The following steps were taken to create the action:
- The
fetchDataAction
function takesdispatch
props as a parameter. - Because our function is asynchronous, we would be using
async
andawait
. - We create a variable(
URL
) that holds our API endpoint. - We have another variable named
data
that holds the response from the API. - Then, we store the JSON response in
dataJSON
after we have gotten the response in JSON format by callingdata.json()
. - Lastly, we return a dispatch function that has a property of
type
and a string ofFETCH_DATA
. It also has apayload()
._embedded.episodes
is the array of the episodes object from ourendpoint
.
Note that the fetchDataAction
function fetches our endpoint, converts it to JSON
objects, and returns the dispatch function, which updates the state declared earlier in the Store.
The exported dispatch type is set to React.Dispatch
. Note that React.Dispatch
is the standard type for the dispatch function according to the @types/react
code base, while
is an array of the Interface Action.
EpisodesList Component
In order to maintain the reusability of our app, we will keep all fetched episodes in a separate file, and then import the file in our homePage
component.
In the components
folder, create an EpisodesList.tsx
file, and copy and paste the following code to it:
import React from 'react'
import { IEpisode, IProps } from '../types/interfaces'
const EpisodesList = (props: IProps): Array => {
const { episodes } = props
return episodes.map((episode: IEpisode) => {
return (
{episode.name}
Season: {episode.season} Number: {episode.number}
)
})
}
export default EpisodesList
- We import
IEpisode
andIProps
frominterfaces.tsx
. - Next, we create an
EpisodesList
function that takes props. The props will have a type ofIProps
while the function has a type ofArray
.
Visual Studio Code suggests that our function type be written as JSX.Element[]
.

While Array
is equal to JSX.Element[]
Array
is called the generic identity. Hence, the generic pattern will be used often in this article.
- Inside the function, we destructure the
episodes
fromprops
which has theIEpisode
as a type.
Read about the generic identityThis knowledge will be needed as we proceed.
- We returned the
episodes
props and mapped through it to return a few HTML tags. - The first section holds the
key
which isepisode.id
and aclassName
ofepisode-box
which will be created later. We know that our episodes have images; hence, the image tag. - The image has a ternary operator that checks if there’s either an
episode.image
or anepisode.image.medium
. Else, we display an empty string if no image is found. Also, we included theepisode.name
in a div.
In section
we show the season that an episode belongs to and its number. We have a button with the text Fav
. We’e exported the EpisodesList
component so that we can use it across our app.
Home Page Component
We want the home page to trigger the API call and display the episodes using the EpisodesList
component we created. Inside the components
folder, create the HomePage
component, and copy and paste the following code to it:
import React, { useContext, useEffect, lazy, Suspense } from 'react'
import App from '../App'
import { Store } from '../Store'
import { IEpisodeProps } from '../types/interfaces'
import { fetchDataAction } from '../Actions'
const EpisodesList = lazy(() => import('./EpisodesList'))
const HomePage = (): JSX.Element => {
const { state, dispatch } = useContext(Store)
useEffect(() => {
state.episodes.length === 0 && fetchDataAction(dispatch)
})
const props: IEpisodeProps = {
episodes: state.episodes,
store: { state, dispatch }
}
return (
<Suspense fallback={loading...}>
)
}
export default HomePage
- We import
useContext
useEffect
lazy
andSuspense
from React. The imported app component is the bedrock upon which all other components must receive the value of the store. - We also import
Store
IEpisodeProps
andFetchDataAction
from their respective files. - We import the
EpisodesList
component using theReact.lazy
feature available in React 16.6.
React lazy loading supports the code-splitting convention. Thus, our EpisodesList
component is loaded dynamically, instead of being loaded at once, thereby improving the performance of our app.
- We destructure the
state
anddispatch
as props from theStore
. - The ampersand (&&) in the
useEffect
hook checks if our episodes state isempty
(or equal to 0). Else, we return thefetchDataAction
function. - Lastly, we return the
App
component. Inside it, we use theSuspense
wrapper, and setfallback
to a div with theloading
text. This will be displayed to the user while we await the response from the API. - The
EpisodesList
component will mount when the data is available, and the data that will contain theepisodes
is what we spread into it.
Set Up Index.txs
The Homepage
component needs to be a child of the StoreProvider
. We’ll have to do that in the index
file. Rename index.js
to index.tsx
and paste the following code:
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import { StoreProvider } from './Store'
import HomePage from './components/HomePage'
ReactDOM.render(
,
document.getElementById('root')
)
We import StoreProvider
HomePage
and index.css
from their respective files.
We wrap the HomePage
component in our StoreProvider
. This makes it possible for the Homepage
component to access the store, as we saw in the previous section.
We have come a long way. Let’s check what the app looks like, without any CSS.

Create Index.css
Delete the code in the index.css
file and replace it with this:
html {
font-size: 14px;
}
body {
marge: 0;
rembourrage: 0;
font-size: 10px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.episode-layout {
display: flex;
flex-wrap: wrap;
min-width: 100vh;
}
.episode-box {
padding: .5rem;
}
.header {
display: flex;
justify-content: space-between;
background: white;
border-bottom: 1px solid black;
padding: .5rem;
position: sticky;
en haut: 0;
}
Our app now has a look and feel. Here’s how it looks with CSS.

Now we see that our episodes can finally be fetched and displayed, because we’ve adopted TypeScript all the way. Great, isn’t it?
Add Favorite Episodes Feature
Let’s add functionality that adds favorite episodes and that links it to a separate page. Let’s go back to our Store component and add a few lines of code:
Note that the highlighted code is newly added:
import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext
(initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload } case 'ADD_FAV': return { ...state, favourites: [...state.favourites, action.payload] }
default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return
{children} }
To implement the “Add favorite” feature to our app, the ADD_FAV
case is added. It returns an object that holds a copy of our previous state, as well as an array with a copy of the favorite state
with the payload
.
We need an action that will be called each time a user clicks on the FAV
button. Let’s add the highlighted code to index.tx
:
import {
IAction, IEpisode, Dispatch } from './types/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => { const URL = 'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes' const data = await fetch(URL) const dataJSON = await data.json() return dispatch({ type: 'FETCH_DATA', payload: dataJSON._embedded.episodes }) }
export const toggleFavAction = (dispatch: any, episode: IEpisode | any): IAction => { let dispatchObj = { type: 'ADD_FAV', payload: episode } return dispatch(dispatchObj) }
We create a toggleFavAction
function that takes dispatch
and episodes
as parameters, and any
and IEpisode|any
as their respective types, with IAction
as our function type. We have an object whose type
is ADD_FAV
and that has episode
as its payload. Lastly, we just return and dispatch the object.
We will add some more snippets to EpisodeList.tsx
. Copy and paste the highlighted code:
import React from 'react' import { IEpisode, IProps } from '../types/interfaces' const EpisodesList = (props: IProps): Array
=> { const { episodes, toggleFavAction, favourites, store } = props const { state, dispatch } = store
return episodes.map((episode: IEpisode) => { return (
{episode.name} Seasion: {episode.season} Number: {episode.number}<button type='button'onClick={() => toggleFavAction(state, dispatch, episode)} > {favourites.find((fav: IEpisode) => fav.id === episode.id) ? 'Unfav' : 'Fav'}
) }) } export default EpisodesList
We include togglefavaction
favorites
and store
as props, and we destructure state
a dispatch
from the store. In order to select our favorite episode, we include the toggleFavAction
method in an onClick
event, and pass the state
dispatch
and episode
props as arguments to the function.
Lastly, we loop through the favorite
state to check if fav.id
(favorite ID) matches the episode.id
. If it does, we toggle between the Unfav
and Fav
text. This helps the user know if they have favorited that episode or not.
We are getting close to the end. But we still need a page where favorite episodes can be linked to when the user chooses among the episodes on the home page.
If you’ve gotten this far, give yourself a pat on the back.
Favpage Component
In the components
folder, create a FavPage.tsx
file. Copy and paste the following code to it:
import React, { lazy, Suspense } from 'react'
import App from '../App'
import { Store } from '../Store'
import { IEpisodeProps } from '../types/interfaces'
import { toggleFavAction } from '../Actions'
const EpisodesList = lazy(() => import('./EpisodesList'))
export default function FavPage(): JSX.Element {
const { state, dispatch } = React.useContext(Store)
const props: IEpisodeProps = {
episodes: state.favourites,
store: { state, dispatch },
toggleFavAction,
favourites: state.favourites
}
return (
<Suspense fallback={loading...}>
)
}
To create the logic behind choosing favorite episodes, we’ve written a little code. We import lazy
and Suspense
from React. We also import Store
IEpisodeProps
and toggleFavAction
from their respective files.
We import our EpisodesList
component using the React.lazy
feature. Lastly, we return the App
component. Inside it, we use the Suspense
wrapper, and set a fallback to a div with the loading text.
This works similar to the Homepage
component. This component will access the store to obtain the episodes the user has favorited. Then, the list of episodes gets passed to the EpisodesList
component.
Let’s add a few more snippets to the HomePage.tsx
file.
Include the toggleFavAction
from ../Actions
. Also include the toggleFavAction
method as props.
import React, { useContext, useEffect, lazy, Suspense } from 'react' import App from '../App' import { Store } from '../Store' import { IEpisodeProps } from '../types/interfaces'
import { fetchDataAction, toggleFavAction } from '../Actions'
const EpisodesList = lazy
(() => import('./EpisodesList')) const HomePage = (): JSX.Element => { const { state, dispatch } = useContext(Store) useEffect(() => { state.episodes.length === 0 && fetchDataAction(dispatch) }) const props: IEpisodeProps = { episodes: state.episodes, store: { state, dispatch }, toggleFavAction, favourites: state.favourites
} return (
<Suspense fallback={ ) } export default HomePageloading...}>
Our FavPage
needs to be linked, so we need a link in our header in App.tsx
. To achieve this, we use Reach Routera library similar to React Router. William Le explains the differences between Reach Router and React Router.
In your CLI, run npm install @reach/router @types/reach__router
. We are installing both the Reach Router library and reach-router
types.
Upon successful installation, import Link
from @reach/router
.
import React, { useContext, Fragment } from 'react' import { Store } from './tsx'
import { Link } from '@reach/router'
const App = ({ children }: { children: JSX.Element }): JSX.Element => {
const { state } = useContext(Store)
return (
Money Heist
Pick your favourite episode
Home Favourite(s): {state.favourites.length}{children} ) } export default App
We destructure the store from useContext
. Lastly, our home will have a Link
and a path to /
while our favorite has a path to /faves
.
{state.favourites.length}
checks for the number of episodes in the favorites states and displays it.
Finally, in our index.tsx
file, we import the FavPage
and HomePage
components, respectively, and wrap them in the Router
.
Copy the highlighted code to the existing code:
import React from 'react' import ReactDOM from 'react-dom' import './index.css' import { StoreProvider } from './Store'
import { Router, RouteComponentProps } from '@reach/router' import HomePage from './components/HomePage' import FavPage from './components/FavPage' const RouterPage = ( props: { pageComponent: JSX.Element } & RouteComponentProps ) => props.pageComponent
ReactDOM.render(
<RouterPage pageComponent={ } path='/' /> <RouterPage pageComponent={ } path='/faves' /> , document.getElementById('root') )
Now, let’s see how the implemented ADD_FAV
works.

Remove Favorite Functionality
Finally, we will add the “Remove episode feature”, so that when the button is clicked, we toggle between adding or removing a favorite episode. We will display the number of episodes added or removed in the header.
STORE
To create the “Remove favorite episode” functionality, we will add another case in our store. So, go over to Store.tsx
and add the highlighted code:
import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext
(initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload } case 'ADD_FAV': return { ...state, favourites: [...state.favourites, action.payload] } case 'REMOVE_FAV': return { ...state, favourites: action.payload }
default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return
{children} }
We add yet another case named REMOVE_FAV
and return an object containing the copy of our initialState
. Also, the favorites
state contains the action payload.
ACTION
Copy the following highlighted code and paste it in action.ts
:
import
{ IAction, IEpisode, IState, Dispatch } from './types/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => { const URL = 'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes' const data = await fetch(URL) const dataJSON = await data.json() return dispatch({ type: 'FETCH_DATA', payload: dataJSON._embedded.episodes }) } //Add IState withits type
export const toggleFavAction = (state: IState, dispatch: any, episode: IEpisode | any): IAction => { const episodeInFav = state.favourites.includes(episode)
let dispatchObj = { type: 'ADD_FAV', payload: episode }
if (episodeInFav) { const favWithoutEpisode = state.favourites.filter( (fav: IEpisode) => fav.id !== episode.id ) dispatchObj = { type: 'REMOVE_FAV', payload: favWithoutEpisode }
} return dispatch(dispatchObj) }
We import the IState
interface from ./types/interfaces
because we’ll need to pass it as the type to the state
props in the toggleFavAction
function.
An episodeInFav
variable is created to check if there’s an episode that exists in the favorites
state.
We filter through the favorites state to check if a favorite ID doesn’t equal an episode ID. Thus, the dispatchObj
is reassigned a type of REMOVE_FAV
and a payload of favWithoutEpisode
.
Let’s preview the result of our app.
Conclusion
In this article, we’ve seen how to set up TypeScript in a React project, and how to migrate a project from vanilla React to TypeScript.
We’ve also built an app with TypeScript and React to see how TypeScript is used in React projects.
I trust you were able to learn a few things.
Please do share your feedback and experiences with TypeScript in the comments section below. I’d love to see what you come up with!
The supporting repository for this article is available on GitHub.
References
- “How To Migrate A React App To TypeScript,” Joe Previte
- “Why And How To Use TypeScript In Your React App?,” Mahesh Haldar

Source link