Using Typescript with React - A Beginner's Guide

Original author: Ben McMahen
  • Transfer
Friends, on the eve of the weekend we want to share with you another interesting publication that we want to coincide with the launch of a new group on the course "JavaScript Developer" .



Having spent the past few months developing React applications and libraries using Typescript, I decided to share some of the things I learned during that time. In this guide, I will tell you about the templates that I use for Typescript and React in 80% of cases.

Should I Learn Typescript for React Application Development? Worth, still worth it! For myself, I realized in practice that strict typing leads to writing much more reliable code, fast development, especially in large projects. At first, you will probably be disappointed, but as you work, you will find that at least a minimal template will really be very helpful.

And if you are stuck on something, remember that you can always type something like any. Any is your new friend. And now we will pass directly to examples.

Your core react component with typescript


What does the standard react component look like on typescript? Let's compare it with the react component in javascript.

import React from 'react'
import PropTypes from 'prop-types'
export function StandardComponent({ children, title = 'Dr.' }) {
  return (
    
{title}: {children}
) } StandardComponent.propTypes = { title: PropTypes.string, children: PropTypes.node.isRequired, }

And now the typescript version:

import * as React from 'react'
export interface StandardComponentProps {
  title?: string
  children: React.ReactNode
}
export function StandardComponent({
  children,
  title = 'Dr.',
}: StandardComponentProps) {
  return (
    
{title}: {children}
) }

Very similar, right? We replaced it propTypeswith an interface typescript.

The title propremains optional, while the heir's prop is still required. We exported our interface in case another component needed a link to it.

Extending Standard HTML Attributes


If we want the parent component to be able to provide additional typed attributes div, such as aria-hidden, styleor className, we can define them in interfaceor extend the built-in interface. In the example below, we say that our component accepts any standard properties divin addition to the header and descendants.

import * as React from 'react'
export interface SpreadingExampleProps
  extends React.HTMLAttributes {
  title?: string
  children: React.ReactNode
}
export function SpreadingExample({
  children,
  title = 'Dr.',
  ...other
}: SpreadingExampleProps) {
  return (
    
{title}: {children}
) }

Event handling


We can typify event handlers to make sure the event argument is of the correct type. The example below demonstrates various ways to achieve this goal:

export interface EventHandlerProps {
  onClick: (e: React.MouseEvent) => void
}
export function EventHandler({ onClick }: EventHandlerProps) {
  // handle focus events in a separate function
  function onFocus(e: React.FocusEvent) {
    console.log('Focused!', e.currentTarget)
  }
  return (
    
  )
}

Not sure which argument signature to use? In the editor, hover over the corresponding property of the event handler.

Using generics with react components


This is a more advanced feature, but it is really powerful. Typically, you define data types in react components with specific attributes. Suppose your component needs an object profile.

interface ProfileType {
  name: string
  image: string
  age: number | null
}
interface ProfilesProps {
  profiles: Array
}
function Profiles(props: ProfilesProps) {
  // render a set of profiles
}

Now let's imagine that you have a component that can accept an array of any type. Generics are like mailing parcels. The courier (our component) does not need to know the contents of the package that you are sending, but the sender (parent component) expects the recipient to receive the content that he sent.

We implement it like this:

interface GenericsExampleProps {
  children: (item: T) => React.ReactNode
  items: Array
}
export function GenericsExample({
  items,
  children,
}: GenericsExampleProps) {
  return (
    
{items.map(item => { return children(item) })}
) }

A bit strange example ... nonetheless, it demonstrates the essence. The component accepts an array of elements of any type, passes through it and calls the function childrenas a render function with an array element. When our parent component provides a render renderer as an inheritor, the element will be typed correctly!

Not understood? This is normal. I myself have not figured out generics to the end, but you are unlikely to need to understand them thoroughly. However, the more you work with typescript, the more it will make sense.

Typing hooks


Hooks mostly work out of the box. Two exceptions can be only useRefand useReducer. The example below shows how we can type refs.

import * as React from 'react'
interface HooksExampleProps {}
export function HooksExample(props: HooksExampleProps) {
  const [count, setCount] = React.useState(0)
  const ref = React.useRef(null)
  // start our timer
  React.useEffect(
    () => {
      const timer = setInterval(() => {
        setCount(count + 1)
      }, 1000)
      return () => clearTimeout(timer)
    },
    [count]
  )
  // measure our element
  React.useEffect(
    () => {
      if (ref.current) {
        console.log(ref.current.getBoundingClientRect())
      }
    },
    [ref]
  )
  return 
Count: {count}
}

Our state is automatically typed, but we manually typed ref to indicate that it will matter nullor contain an element div. When we refer to ref in a function useEffect, we need to make sure that it is not equal null.

Gearbox typing


With the gearbox is a little more complicated, but if it is properly typed, then this is great.

// Yeah, I don't understand this either. But it gives us nice typing
// for our reducer actions.
type Action = V extends void ? { type: K } : { type: K } & V
// our search response type
interface Response {
  id: number
  title: string
}
// reducer actions. These are what you'll "dispatch"
export type ActionType =
  | Action<'QUERY', { value: string }>
  | Action<'SEARCH', { value: Array }>
// the form that our reducer state takes
interface StateType {
  searchResponse: Array
  query: string
}
// our default state
const initialState: StateType = {
  searchResponse: [],
  query: '',
}
// the actual reducer
function reducer(state: StateType, action: ActionType) {
  switch (action.type) {
    case 'QUERY':
      return {
        ...state,
        query: action.value,
      }
    case 'SEARCH':
      return {
        ...state,
        searchResponse: action.value,
      }
  }
}
interface ReducerExampleProps {
  query: string
}
export function ReducerExample({ query }: ReducerExampleProps) {
  const [state, dispatch] = React.useReducer(reducer, initialState)
  React.useEffect(
    () => {
      if (query) {
        // emulate async query
        setTimeout(() => {
          dispatch({
            type: 'SEARCH',
            value: [{ id: 1, title: 'Hello world' }],
          })
        }, 1000)
      }
    },
    [query]
  )
  return state.searchResponse.map(response => (
    
{response.title}
)) }

Use typeofand keyofto typify component options


Suppose we need a button that can have a different appearance, each of which is defined in an object with a set of keys and styles, for example:

const styles = {
  primary: {
    color: 'blue',
  },
  danger: {
    color: 'red',
  },
}

Our button component must accept a property type, which can be
any key from the object styles(for example, "primary" or "danger" ). We can type it quite simply:

const styles = {
  primary: {
    color: 'blue',
  },
  danger: {
    color: 'red',
  },
}
// creates a reusable type from the styles object
type StylesType = typeof styles
// ButtonType = any key in styles
export type ButtonType = keyof StylesType
interface ButtonProps {
  type: ButtonType
}
export function Button({ type = 'primary' }: ButtonProps) {
  return 
}

These examples will help you go 80% of the way. If you’re stuck, it’s often worth it to
just look at existing open source examples.

Sancho UI is a set of react components
built with typescript and emotion.
Blueprint is another set of components
reactbuilt on typescript.

Well, according to established tradition, we are waiting for your comments.

Also popular now: