Mastering Typescript for React Hooks
Tags: react, javascript, typescript
Published: 2021-02-03 19:21:09

---
title: Mastering Typescript for React Hooks
published: true
date: 2021-02-03 15:43:05 UTC
tags: reactjs,javascript,typescript,react
canonical_url: 
---

### Mastering TypeScript for React Hooks

So you want to use TypeScript in your React application, but even the hooks are giving you grief. Well, let’s get you comfortable with how to use TypeScript typing with those hooks and get you on your way.

![](https://cdn-images-1.medium.com/max/1024/1*8BEPuAzdDn1WqkM1P-sgew.jpeg)<figcaption>Banner image</figcaption>

This article is meant to supplement the excellent [React TypeScript Cheat Sheet](https://github.com/typescript-cheatsheets/react) which you should definitely take a look at.

### useState

`useState` is a fun one because we use it all the time and most of the time it’s fine, until it’s not. Take this example:

```ts
const [myNumber, myNumberSet] = useState(10);

const onClick = () => myNumberSet(20);
```

TypeScript is totally fine with this because the typing on `useState` looks at the initial value, sees that it’s a `number` and sets this type to this:

```ts
const [myNumber, myNumberSet] = useState<number>(10);
```

So any number is going to be fine.

The problem comes up when you have something like this:

```ts
const [myAccount, myAccountSet] = useState(null);

const onAuthResponse = () => myAccountSet({ user: "foo", ... });
```

TypeScript has no idea that what you initially set to `null` could potentially be an account record. So what you need to do is tell it that:

```ts
interface IAccount {
  user: string;
  ...
}
const [myAccount, myAccountSet] = useState<IAccount | null>(null);

const onAuthResponse = () => myAccountSet({ user: "foo", ... });
```

Now TypeScript understands that your `myAccount` value can either be `null` or an object that matches the typing of `IAccount`.

A similar issue happens with arrays. Take this example:

```ts
const [myNumbers, myNumbersSet] = useState([]);

const onClick = () => myNumbersSet([10, 20, 30]);
```

TypeScript is going to give you a really odd error about trying to use a `number[]` when a `never[]` is expected. Which actually makes sense because, as far as TypeScript knows the only valid value is an empty array (i.e. `never[]`). It has no idea you intend to store numbers in there.

So the fix for this is to type it

```ts
const [myNumbers, myNumbersSet] = useState<number[]>([]);

const onClick = () => myNumbersSet([10, 20, 30]);
```

And now TypeScript will be happy again because even an empty array is a valid type of `number[]`.

### useEffect

The great thing about `useEffect` that that it doesn’t take any types. So if you are looking to make sure you are typing it correctly, fear not, you are.

If you want to check that for yourself then right click on the word `useEffect` in your VS Code and use the `Go to Type Definition` command to go to where `useEffect` is defined in the React source.

`useEffect` takes two arguments, the first is a function with no parameters that either returns `void`, or returns another function (the cleanup function), which takes no arguments and returns a `void`.

IMHO, using `Go to Type Definition` should be your first stop any time you run into an issue in TypeScript.

### useContext

Getting `useContext` typed properly really comes down to getting the `createContext` call typed properly. For example, you might have something like this:

```ts
const MyContext = createContext(null);
```

Which basically leaves TypeScript with no clue about what could potentially be in the context and so it leaves it at; the context must always contain `null`. Which is probably not what you want.

The easiest way to handle this would be, if you want either `null` or some data, to define it like this:

```ts
interface IMyContextState {
  userID: string;
}
const MyContext = createContext<IMyContextState | null>(null);
```

Which tells TypeScript that the context must either contain an object that matches `IMyContextState` or `null`.

If you have a default state it gets a lot easier:

```ts
const myDefaultState = {
  userID: "";
}

export type MyContextType = typeof myDefaultState;

const MyContext = createContext(myDefaultState);

export default MyContext;
```

In this case we don’t need to tell TypeScript that the context has the types in `myDefaultState` it already knows that, but we now export the schema of the default state as `MyContextType`. So that we can then use it when we call `useContext` like so:

```ts
import MyContext, { MyContextType } from './store';
...
const ctx:MyContextType = useContext(MyContext);
```

The typing of `ctx` is a bit of overkill in this case because `useContext` already knows the types from `MyContext` and you can just get away with:

```ts
import MyContext from './store';
...
const ctx = useContext(MyContext);
```

### useReducer

Typing `useReducer` is a lot like typing Redux, so it’s a two-fer, if you get this right, you are that much closer to Redux typing. So `useReducer` takes two things, the `reducer` function and the initial state. Let’s start off with the initial state:

```ts
const initialState = {
  counter: 0,
};
```

Next we need some actions. Now in Javascript we wouldn’t type these at all but in TypeScript we can and we should type them, and that would look like this:

```ts
type ACTIONTYPES =
  | { type: "increment"; payload: number; }
  | { type: "decrement"; payload: number; };
```

And then the reducer is going to look something like this:

```ts
function myReducer(state: typeof initialState, action: ACTIONTYPES) {
  ...
}

const [state, dispatch] = useReducer(myReducer, initialState);
```

And this will give you hinting on the state and also ensure that any call to dispatch will need to match one of the variants in `ACTIONTYPES`.

### useRef

Typing `useRef`, particularly when it comes to use refs with DOM elements, which is a pretty common use case is straightforward. Let’s say you have something like this:

```ts
return (<input ref={inputRef} />);
```

In your code then the corresponding `useRef` would look like this:

```ts
const inputRef = useRef<HTMLInputElement | null>(null);
```

And specifying the types isn’t 100% necessary here either. The only trick is to make sure you get the right type for the corresponding DOM element.

If you are going to use a ref to hold data then you can do something like this:

```ts
const intervalRef = useRef<number | null>(null);
```

If you are, for example, holding a reference to an interval.

### useMemo

The typing on `useMemo` is all about what is produced by the factory function that you put in there. For example:

```ts
const [numbers] = useState([1,2,3,4]);
const filteredNums = useMemo(
  () => numbers.filter(n => n > 2),
  [numbers]
);
```

In this case the typing on `filteredNums` is inferred by TypeScript to be `number[]` because of the output of the factory function. If you wanted to type it you could do:

```ts
const filteredNums: number[] = useMemo(
  () => numbers.filter(n => n > 2),
  [numbers]
);
```

But you really don’t need to. TypeScript is very, very good at figuring out the return type of a function. In fact, if you want to you can use the `ReturnType` utility type to get the return type from a function like so:

```ts
type MyFunctionReturnType = ReturnType<typeof myFunction>;
```

You can find more information on the [amazing array of utility types on the TypeScript language site](https://www.typescriptlang.org/docs/handbook/utility-types.html).

### Video Version

If you want to see an in-depth walk through of a lot of this information and much more check out the associated YouTube video:

{% youtube zM_ZiSl2n2E %}

### Conclusions

The more I work with TypeScript and React the more that I am convinced that it’s worth the investment. You get the benefits of hinting as you code. You are communicating your intent through the types. And you get the benefits of a type safety check at compile time.

Hopefully this article will help you realize these benefits as you try out using TypeScript in your React projects and you learn to master the typing of your React hooks.

* * *