Typer React avec TypeScript

Voir la vidéo
Description Sommaire

Dans ce chapitre nous allons voir comment utiliser le types react @types/react.

00:48 Config tsconfig.json
02:24 Typer les props
07:05 Typer useState
09:51 Typer useRef
11:46 Les évènements
12:52 forwardRef
14:31 Type ReactNode
16:54 Typer un composant
18:43 Typer un contexte

La configuration

React n'intègre pas de types par défaut et on commencera par installer @types/react et on modifiera la configuration la clef jsx dans le fichier tsconfig.json.

{
  "jsx": "react-jsx"
}

Cette configuration permettre de remplacer le jsx par un appel à la fonction jsx de react/jsx-runtime.

import { jsx as _jsx } from "react/jsx-runtime";
import React from 'react';
export const HelloWorld = () => _jsx("h1", { children: "Hello world" });

Typer un composant

Par défaut une fonction qui renvoit du JSX sera considéré comme un composant.

type Props: {
  start: number
}
function Counter ({start}: Props) {
  return <div>Hello World</div>
} 

Si votre composant peut recevoir des enfants, alors vous pouvez utiliser le type PropsWithChildren

type Props: PropsWithChildren<{
  start: number
}>

Il est aussi possible d'être plus explicite en utilisant le type FunctionComponent, ce type peut être intéréssant pour des retour de composant d'ordre supérieur.

type Props: {
  start: number
}
const Counter: FunctionComponent<Props> = ({start}) => {
  return <div>Hello World</div>
} 

Les hooks

Les hooks sont utilisable avec un générique pour indiquer le type de valeur qu'ils vont gérer mais il est inféré de base par les paramètres.

const [n, setN] = useState(3) // Le type sera automatiquement un nombre
const [i, setI] = useState<number>() // Le type sera number | undefined

Pour les ref il faudra penser à mettre une valeur null par défaut pour qu'il puisse être inclu dans la prop ref

const ref = useRef<HTMLButtonElement>(null)

On peut aussi utiliser le type RefObject<T> pour représenter un objet de type ref.

forwardRef

La méthode forwardRef elle aussi contient 2 génériques pour définir le format des props et de la ref.

export const Counter = forwardRef<HTMLButtonElement, Props>(({start, children}, ref) => {
    // ....
})

Typer les composants

Vous avez plusieurs types qui sont intéréssant en connaitre en fonction de certaines situation.

  • ReactNode, permet de représenter un noeud (un morceau de JSX, une chaine, null ou undefined)
  • JSX.IntrinsicElements est un type interne qui permet de représenter tous les éléments HTML acceptés par React. On peut par exemple utiliser keyof JSX.IntrinsicElements si on veut qu'une props accepte un tag HTML.
  • ComponentType<Props> représente un composant (une fonction ou une class) qui pourra être utilisé comme élément JSX. On peut ajouter un générique pour définir les props qui sont attendues

Le contexte

Pour les contextes, 2 situations se présentent en général.

Contexte avec état par défaut

Dans ce cas là le type va automatiquement être inféré depuis l'objet qui sera utilisé dans l'état par défaut. On n'hésitera pas à utiliser as afin d'avoir un type le plus précis possible.

const CounterContext = createContext({
  n: 0,
  incrementer: (step: number) => void,
  type: 'card' as 'card' | 'text'
})
export const CounterProvider = () => {
    const [n, setN] = useState(0)
    const incr = useCallback(() => setN(n => n+1), [])
    return <CounterContext.Provider value={{n, incr, type: 'card'}}>
        {children}
    </CounterContext.Provider>
}
export const useCounter = () => {
    return useContext(CounterContext);
}

Les vérifications se feront alors automatiquement :

  • Dans le CounterProvider, la value devra avoir la même forme que l'objet passé en paramètre de createContext
  • Le useContext retournera un objet qui aura la même forme que l'objet passé en paramètre de createContext

Contexte sans état par défaut

Dans ce cas là on utilisera un type pour définir la forme des valeurs au sein de notre contexte et on utilisera un générique lors de l'utilisation de createcontext()

type ContextProps = {
    n: number,
    incr: () => void
}
const CounterContext = createContext<null | ContextProps>(null)

Afin d'éviter des problèmes lors de l'utilisation de ce contexte, on s'assurera de mettre une erreur explicite.

export const useCounter = () => {
    const value = useContext(CounterContext);
    if (value === null) {
        throw new Error('Vous devez entourer ce composant d\'un <CounterProvider>')
    }
    return value;
}
Publié
Technologies utilisées
Auteur :
Grafikart
Partager