Typescript benefits without... using Typescript!
Tags: javascript, typescript, redux, react
Published: 2020-11-09 12:29:15

Many developers wonder, whether they should use Javascript or Typescript for their next projects, or perhaps migrate their current projects to Typescript. What I will try to show in this post though is, that this is not necessarily black and white decision, often you can use Typescript partially, often you already do thanks to editors like **Visual Studio Code**, as most of external libraries ship with TS types, so you already get things like hints and autocomplete, thanks to... Typescript!

## Main reason to switch to Typescript

The bigger application is, the more important is to know types for our variables, functions, classes and so on, as well as to have a guarantee that we use those properly. This is exactly what Typescript gives us in the contrast to pure Javascript. What's interesting though is that you can have part of your app written in Javascript and the rest in Typescript, this is no either or decision, let's see how!

## `useQuery` type

Look at the top picture of this post, notice that we can see `data` type despite the fact this file is written in Javascript! In order to see how this could be achieved, let's write a type for `useQuery` function. Don't worry what it does exactly, this won't be needed to understand this post. If you are curious though, `useQuery` is a React hook which gets a server response from a Redux store. Anyway, going back to writing `useQuery` type:
```ts
interface Response {
  data: any;
  loading: boolean;
  error: any;
}

function useQuery(props: { type: any }): Response;
```

Think about it as a prototype for now, this is by no means finished. First of all, `type` is defined as `any`, we will fix it soon. But there is more important thing, `data` is also of type `any`! But we really cannot define it as a specific type, because `useQuery` is a reusable function! What should we do then? Typescript generics to the rescue!

## `Data` Generic

What are generics? You could think about them as variables for types! Let's add it to `useQuery`:
```ts
interface Response<Data> {
  data: Data;
  loading: boolean;
  error: any;
}

function useQuery<Data>(props: { type: any }): Response<Data>;
```

Now, we could use it like:
```ts
interface User {
  id: string;
  username: string;
}

const {
  data,
  loading,
  error,
} = useQuery<User>({ type: fetchUser });
```

Ok, but this is different than advertised at the beginning! First of all, we provide `User` interface to `useQuery`. Secondly, you can pass generics only in Typescript files! Before we fix that, let's solve `type: any` in `useQuery`. What is `fetchUser`? This is nothing else than Redux action creator! Actually this is a specific Redux action creator, which creates so called `RequestAction` from [`redux-requests` library](https://github.com/klis87/redux-requests). Let's use this information to improve `useQuery` type:
```ts
import { RequestAction } from '@redux-requests/core';

interface Response<Data> {
  data: Data;
  loading: boolean;
  error: any;
}

function useQuery<Data>(props: { type: () => RequestAction }): Response<Data>;
```

How does it help us with `Data` generic though? It turns out that `RequestAction` also has an optional `Data` generic. This is hard to explain verbally, but Typescript can intelligently deduct that passed generics could be connected, which is related to *type inference* concept!

## Generics type inference

So what we want to achieve is to have `data` typed without passing `Data` generic to `useQuery`. For a start, we need to make `Data` generic optional then:
```ts
import { RequestAction } from '@redux-requests/core';

interface Response<Data> {
  data: Data;
  loading: boolean;
  error: any;
}

function useQuery<Data = any>(props: { type: () => RequestAction }): Response<Data>;
```

We did it by appending `= any` to `Data` generic. Now, let's pass `Data` generic to `RequestAction`:
```ts
import { RequestAction } from '@redux-requests/core';

interface Response<Data> {
  data: Data;
  loading: boolean;
  error: any;
}

function useQuery<Data = any>(props: {
  type: () => RequestAction<Data>;
}): Response<Data>;
```

This is where the magic happens! The key here is that `useQuery` and `RequestAction` use the very same generic! Now, if a generic is passed to `type` function, then `useQuery` will pick it automatically! Let's see this in practice:
```ts
import { RequestAction } from '@redux-requests/core';

interface User {
  id: string;
  username: string;
}

export function fetchUser(): RequestAction<User> {
  return { type: 'FETCH_USER' };
}
```

We don't need to think about `fetchUser` implementation, all that matters is that it has `User` generic passed. Now, `useQuery` could look like that:
```js
import { useQuery } from '@redux-requests/react';

import { fetchUser } from './actions';

const { data } = useQuery({ type: fetchUser });
```

That's it! This could be even Javascript file and `data` would have `User` type anyway! You don't need to pass `Data` generic to `useQuery` anymore, because it is automatically taken from `fetchUser`.

Of course, `fetchUser` has to be written in Typescript, so you might ask why we would do that. One of the reasons might be that `useQuery` to get `user` object could be used in multiple places, while `fetchUser` had to be declared only once. All of those places would have proper types automatically. Another benefit is that you could have those types even in Javascript files!

All in all, it depends on the use case, but this pattern of reusing generics is definitely worth knowing about. If you are interested in more possible use cases, I recommend you to check [Typescript guide of `redux-requests` library](https://redux-requests.klisiczynski.com/docs/guides/usage-with-typescript). It takes this concept even further, for example you also get automatic type inference wherever you dispatch *request actions*! Happy JS and TS mixing!