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 utiliserkeyof 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 decreateContext
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;
}