Type React with TypeScript — Learn TypeScript Training

In this chapter we will see how to use the react types @types/react.

00:48 Config tsconfig.json
02:24 Typing props
07:05 Type useState
09:51 Type useRef
11:46 The events
12:52 forwardRef
14:31 Type ReactNode
16:54 Type a component
18:43 Type a context

The configuration

React does not include default types and we will start by installing @types/react and we will modify the configuration the jsx key in the file tsconfig.json.

{
  "jsx": "react-jsx"
}

This configuration allows to replace the jsx by a call to the jsx function of react/jsx-runtime.

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

Type a component

By default a function that returns JSX will be considered a component.

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

If your component can receive children, then you can use the type PropsWithChildren

type Props: PropsWithChildren<{
  start: number
}>

It is also possible to be more explicit by using the type FunctionComponentthis type can be interesting for higher order component returns.

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

The hooks

The hooks can be used with a generic to indicate the type of value they will handle, but it is inferred from the base by the parameters.

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

For the ref it will be necessary to think of putting a null value by default so that it can be included in the prop ref

const ref = useRef<HTMLButtonElement>(null)

You can also use the type RefObject<T> to represent an object of type ref.

forwardRef

The method forwardRef it also contains 2 generics to define the format of the props and the ref.

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

Type components

You have several types that are interesting to know depending on certain situations.

  • ReactNodeallows to represent a node (a piece of JSX, a chain, null or undefined)
  • JSX.IntrinsicElements is an internal type that allows to represent all HTML elements accepted by React. One can for example use keyof JSX.IntrinsicElements if you want a prop to accept an HTML tag.
  • ComponentType<Props> represents a component (a function or a class) that can be used as a JSX element. You can add a generic to define the props that are expected

The context

For the contexts, 2 situations generally arise.

Context with default state

In this case the type will automatically be inferred from the object that will be used in the default state. We will not hesitate to use as in order to have the most precise type 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);
}

The checks will then be done automatically:

  • In the CounterProvider, the value must have the same form as the object passed as a parameter of createContext
  • the useContext will return an object that will have the same shape as the object passed as a parameter of createContext

Default stateless context

In this case we will use a type to define the form of the values ​​within our context and we will use a generic when using createcontext()

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

In order to avoid problems when using this context, we will make sure to put an explicit error.

export const useCounter = () => {
    const value = useContext(CounterContext);
    if (value === null) {
        throw new Error('Vous devez entourer ce composant d\'un <CounterProvider>')
    }
    return value;
}