Most Common React App Types using TypeScript

Earlier I wrote some basic posts regarding React & TypeScript and how to set up your React App to use TypeScript. Now I will show you the most common types you are going to use while writing your React App.

So let's dive into it.


Basic Props

To get started I'm going to show how you can type basic props. For every typed language, we must handle some common values like strings and numbers. Here's how you handle them in TypeScript:

For more and detailed options, you can check the official docs.


Typing Components

Now we are going to check how to type the component itself. In React we have two types of components: Stateless and Class component. Briefly, Stateless components don't have either state or lifecycle methods, it's just a JavaScript function that returns a React Component.

export default const MyStatelessComponent = ({
userName,
age,
isActive
}) =>
<div>
<span>{`Hello, ${userName}`}</span>
</div>

Typing Stateless Component

Basically, you can add an interface containing all the props that your component receives and then assign it to its params definition using React.SFC.

interface Props {
userName: string
age: number
isActive: boolean
}
export const MyStatelessComponent = ({
userName,
age,
isActive,
}: Props) => (
<div>
<span>{`Hello, ${userName}`}</span>
</div>
)

So now, every time you use MyStatelessComponent it will request userName, age, and isActive props.

<MyStatlessComponent userName='Josh' age={23) isActive={false} />

Typing Stateless Components using SFC

Although, I suggest to use this another option provided by React, SFC (Stateless Functional Component).

SFC is designed to support the most common Stateless Components props, so it already handles definitions like children for you. Since it’s an interface, you can also extend it to your custom Stateless Component props definition.

const MyStatelessComponent: React.SFC<Props> = ({ userName, age, isActive }) => { … }

Typing Class Components Props

React also has class components, which extends from either Component or PureComponent. They contain some features like lifecycle methods, state and constructor. To type a React class component you have to declare your interface just as we did in the first stateless component example, but to bind it it's a little different. Let’s take a look at it:

interface Props {
userName: string
age: number
isActive: boolean
}
class MyClassComponent extends React.PureComponent<Props> { }

Typing Class Component State

When dealing with class components we generally need to handle its state as well, so we can create a State interface and add it to the component type definition:

interface Props {}
interface State {
isLoading: boolean
}
class MyClassComponent extends React.PureComponent<Props, State> {
constructor(props: Props): {
super(props);
this.state = {
isLoading: false,
}
}
}

Every state prop now must be included into State interface.


Great! Now we know how to type common props like strings and numbers, and also how to bind them into our stateless or class components.

But when we are working in the web with React, we commonly need to send other types of props, like styles, elements or even React components. So let’s take a look how to correctly type them.


Typing Common React Props

Inline styles

To handle inline styles we can use another helper from React, React.CSSProperties.

interface Props {
style: React.CSSProperties
}
class InlineStyledComponent extends React.PureComponent<Props> { ... }
<InlineStyledComponent style={{ backgroundColor: '#f2f2f2' }}

Styled Components

Basically, when you wrap an element or component with .styled() hoc, it will create a className attribute and add it to your component in order to assign the styles you have written. So, if your component must handle Styled Components you should just add a className prop to your interface as a string.

// SomeComponent.tsx
interface StyledProps {
className?: string
}
class SomeComponent extends React.PureComponent<StyledProps> { ... }
// AnotherComponent.tsx
const SomeComponentStyled = styled(SomeComponent)`
background-color: #f2f2f2;
`
<SomeComponentStyled />

Sometimes we also need to use props inside our styled components and they must also be typed, otherwise TypeScript will prevent you to compile.

Typing Styled Components Props

const DivStyled = styled.div`
background-color: ${(props: DivStyledProps) => props.isActive ? '#f0f' : '#f2f2f2'};
`
interface DivStyledProps {
isActive: boolean
}
<DivStyled isActive={true}/>

React DOM element

You can pass down DOM elements from React by using React.ReactElement<>. However you must provide what type of element you want it to handle. In the example below we will providing a <button> to our component.

interface Props {
button: React.ReactElement<HTMLButtonElement>
}
export default ButtonComponent extends React.PureComponent<Props> { ... }
<ButtonComponent button={<button>Click me</button>} />

React Component (children)

React also provides a interface for React Components it is React.ReactNode. The most common usage is when we must to pass down children to a component. We must type it like this:

interface Props {
children: React.ReactNode
}
export default ComponentWithChildren extends React.PureComponent<Props> { ... }
<ComponentWithChildren><div>Some content</div></ComponentWithChildren>

Important to mention that if you type it as JSX.Element (abstraction of React.ReactElement<any>) or as React.ReactElement<some-type> it might work as well, since React.ReactNode also abstracts React.ReactElement<any>. The difference is that React.ReactNode also handles ReactFragment, ReactPortal, boolean, null or undefined types, which might be more commonly usage when we are dealing with React components.

TL;DR

Prefer using React.ReactNode to type components, since it’s designed to handle common React Component props

Typing to Allow Only Specific Component

Since React.ReactElement<> accepts any type of element, you can also make it strictly to accepts some specific component type. Let’s say you have a Modal component that should only works if it receives a Text component. You can type it as the following

interface TextProps {
children: string
}
const Text = ({ children }: TextProps) => <span>{children}</span>
interface Props {
children: React.ReactElement<Text>
}
export default class Modal extends React.PureComponent<Props> { ... }
// Correct usage
<Modal><Text>Some content</Text></Modal>
// Wrong usage
<Modal><Text>{true}</Text></Modal>

Functions

When we need to handle function props there are several ways to do it, but the most basic is the following:

interface Props {
myFunction: () => void
}

Depending on how your function will work, you can also add types to its props and return. Let’s say we have a sum function, which receives 2 numbers and returns the sum of them. It would be typed as the following:

interface Props {
sum: (firstValue: number, secondValue: number) => number
}

We can also use reuse another interface to better type our functions. The example below shows a way of handling onClick functions from a DOM button element.

interface Props {
onClick?: (e: React.MouseEvent<HTMLElement>) => void
}

Enum

We can also allow a specific prop to accepts only some pre defined values. To achieve that we use enum.

First, we must define our enum like this:

enum AuthenticationMethod {
socialMedia = "SOCiAL_MEDIA",
email = "EMAIL",
phoneNumber = "PHONENUMBER",
}

Then, we assign this enum to our interface prop type:

interface UserProps {
authentication: AuthenticationMethod
}

Now whenever the component is called it will require the authentication prop with either socialMedia, email, or phoneNumber enum value. We should pass it down like this:

<UserLogin authentication={AuthenticationMethod.socialMedia} />

AuthenticationMethod enum must be available in the parent component that’s calling the component that contains the enum prop. You can either declare it again, or import it from the child component or from a type file (which I recommend).

Important to mention: enum cannot be exported from .d.ts file. It only works if it’s exported from a .ts or .js file.

A complete example should looks like this:

// types.ts
export enum AuthenticationMethod {
socialMedia = "SOCiAL_MEDIA",
email = "EMAIL",
phoneNumber = "PHONENUMBER",
}
// UserLogin.tsx
(...)
import { AuthenticationMethod } from 'path-to/types'
(...)
interface UserProps {
authentication: AuthenticationMethod
}
(...)
// Login.tsx
(...)
import { AuthenticationMethod } from 'path-to/types'
import UserLogin from 'path-to/UserLogin'
(...)
<UserLogin authentication={AuthenticationMethod.socialMedia} />
(...)

Using extends

We also might face situations where we need to reuse some Props and add other specific types to it. To handle that TypeScript has the extends keyword. It basically get the props of an interface and assign it to the new one you are creating. Important to mention that type alias can’t extend an interface. You can only extend an interface to a new interface.

Wrong!
interface DefaultProps {
style: any
}
// It will break!
type Props extends DefaultProps = {
userName: string
}
Correct!
interface DefaultProps {
style: any
}
interface Props extends DefaultProps {
userName: string
}

In the example above when you bind Props to a component it will require both style and userName props every time you try to use it.

Extending multiple interfaces

interface DefaultProps {
style: any
}
interface UserProps {
userName: string
}
interface Props extends DefaultProps, userProps {
handleButton: () => void
}

Intersection Operation to Handle Multiple Interfaces

You can also extends a component or element props when binding it by using intersection operation (&). This way you can mix different interfaces into a same component or value. It might looks like this:

interface DefaultProps {
style: any
}
interface Props {
handleButton: () => void
}
const FirstComponent = (props: Props) { ... }
const SecondComponent = (props: Props & DefaultProps) { ... }

Extending Third Party Interfaces

There are several helpful ways of using extends. You can also extend any React interface available. In the example below we are going to extend all props available on the HTML button.

interface Props extends React.HTMLProps<HTMLButtonElement>{
style?: any
}
const ButtonComponent = (props: Props) { ... }
<ButtonComponent onClick={() => console.log('It works!'} />

That’s it for now!

By now you will be able to type most of your React apps cases. You can always find more details in the official docs.

In the next post I’ll provide more examples using other features like typing HOCs, using Partial, Generics, Alias, and more.