Subscribe to 💌 Tiny Improvements, my weekly newsletter for product builders. It's a single, tiny idea to help you build better products.

Refactoring TypeScript React components in VS Code

Extracting types from a JavaScript object is a common task in TypeScript. This post shows how to do it with VS Code.

Extracting types from a JavaScript object

I am brand new to TypeScript, and far from an expert. So far, I really enjoy using it to build React apps with Next.js. As I'm growing the skills I'll need to be a better TypeScript developer, I'm finding that a fairly typical chore for me is to refactor my code to extract types from a JavaScript object. For example, I often find myself writing code in react components that looks a lot like this:

1
import Footer from '@components/Footer';
2
import Header from '@components/Header';
3
4
const PageLayout = ({ children }) => {
5
return (
6
<>
7
<section className="min-h-[100vh] pb-8 sm:pb-12 lg:pb-12">
8
<Header />
9
<div className="lg:relative">{children}</div>
10
</section>
11
<Footer />
12
</>
13
);
14
};
15
16
export default PageLayout;

There is no type-checking going on in this code as it is written. I've been writing react with good ol' plain JavaScript for so long that creating components like this is reflexive for me. I'm not even thinking about it.

Eventually during implementation I get to the point where adding some complexity to the component requires me to add some type-checking. At that point, I usually refactor the component to lambda syntax, and add a type annotation to the component:

1
import Footer from '@components/Footer';
2
import Header from '@components/Header';
3
4
const PageLayout = ({ children }) => {
5
return (
6
<>
7
<section className="min-h-[100vh] pb-8 sm:pb-12 lg:pb-12">
8
<Header />
9
<div className="lg:relative">{children}</div>
10
</section>
11
<Footer />
12
</>
13
);
14
};
15
16
export default PageLayout;

Next, I'll add a type annotation to the children prop:

1
import Footer from '@components/Footer';
2
import Header from '@components/Header';
3
4
const PageLayout = ({ children }: { children: React.ReactNode }) => {
5
return (
6
<>
7
<section className="min-h-[100vh] pb-8 sm:pb-12 lg:pb-12">
8
<Header />
9
<div className="lg:relative">{children}</div>
10
</section>
11
<Footer />
12
</>
13
);
14
};

Taking it a step further, I'll give the function a return type signature, using React.FC and generics to define the props:

1
import Footer from '@components/Footer';
2
import Header from '@components/Header';
3
4
const PageLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
5
return (
6
<>
7
<section className="min-h-[100vh] pb-8 sm:pb-12 lg:pb-12">
8
<Header />
9
<div className="lg:relative">{children}</div>
10
</section>
11
<Footer />
12
</>
13
);
14
};

Up to this point, I don't really mind the way this component was written in any of the previous refactors. It's clearly getting more complex, but I can still understand what's going on. But what if I wanted to add a variant prop to the component? I'd have to add another type annotation to the function signature:

1
import Footer from '@components/Footer';
2
import Header from '@components/Header';
3
4
const PageLayout: React.FC<{
5
children: React.ReactNode;
6
variant: 'dark' | 'light' | 'black' | 'tan';
7
}> = ({ children, variant }) => {
8
return (
9
<>
10
<section className="min-h-[100vh] pb-8 sm:pb-12 lg:pb-12">
11
<Header variant={variant} />
12
<div className="lg:relative">{children}</div>
13
</section>
14
<Footer />
15
</>
16
);
17
};

Even with a little prettier formatting, this is getting a little hard to read. This is where I usually extract the props into a type, and then use that type in the function signature:

1
import Footer from '@components/Footer';
2
import Header from '@components/Header';
3
4
type PageLayoutProps = {
5
children: React.ReactNode;
6
variant: 'dark' | 'light' | 'black' | 'tan';
7
};
8
9
type PageLayout = React.FC<PageLayoutProps>;
10
11
const PageLayout: PageLayout = ({ children, variant }) => {
12
return (
13
<>
14
<section className="min-h-[100vh] pb-8 sm:pb-12 lg:pb-12">
15
<Header variant={variant} />
16
<div className="lg:relative">{children}</div>
17
</section>
18
<Footer />
19
</>
20
);
21
};

Now this I like! The props for this page are defined in a single place, and the function signature is much easier to read.

Using VS Code's refactor menu to extract types

As it works out, VS Code has loads of functionality to make this sort of work quicker. The refactor menu can be accessed by highlighting some code, right clicking on it, and selecting Refactor.... There are a number of TypeScript-specific functions provided which can help you refactor your code.

In this case, we can extract types from our component's signature in a few steps:

VS Code Screenshot: Select the entire `React.FC<` return type and right clicking on it
Select the entire `React.FC<` return type and right click on it, and Choose "Refactor..." from the context menu.
VS Code Screenshot: Choose 'extract type to type alias' from the refactor menu
Select "Extract type to type alias" to extract the type into a type alias
VS Code Screenshot: Give the extracted type a name, and hit enter.
Give the extracted type a name, and hit enter.

Bonus: Add JSDoc comments to your types for better intellisense

If you want to take it further, you can add a JSDoc comment to the type definition to add some documentation. This is a good way to document your code, and it's also a good way to get better intellisense for your code if it's supported in your editor:

1
import Footer from "@components/Footer";
2
import Header from "@components/Header";
3
4
type PageLayoutProps = {
5
children: React.ReactNode;
6
variant: "dark" | "light" | "black" | "tan";
7
};
8
9
/**
10
* The PageLayout component is used to wrap all pages in the application.
11
* It provides a consistent header and footer, and a consistent layout for
12
* the page content.
13
* @param {object} props
14
* @param {React.ReactNode} props.children - The page content
15
* @param {"dark" | "light" | "black" | "tan"} props.variant - The color variant for the page
16
* @returns {JSX.Element}
17
**/
18
const PageLayout: PageLayout = ({ children, title }) => {
19
20
/* etc */

Don't learn TypeScript, just use it

...and by that I mean that TypeScript is a great enhancement to building things with JavaScript, but you do not need to be an expert on TypeScript to use it by any means. I've been starting to use TypeScript more and more in my projects, and I've found that I can get a lot of mileage out of it without having to learn every single detail about it.

Theo from t3.gg has a great video that sums up my learning strategy for TypeScript pretty well, called Don't Learn TypeScript. I highly recommend watching it, or putting it in your queue for your next coffee break:

Resources for learning TypeScript

As I mentioned, I'm fairly new to TypeScript - I only just started using it in the past few months. If you're learning TypeScript and are looking for some great places to learn, check these out:

  • Matt Pocock's YouTube channel is a library of really interesting videos about TypeScript. He shares tutorials as well as experience-driven, nuanced opinions about TypeScript (when should I use an interface instead of a type? etc). IMO he's the defacto expert on TypeScript in real life.
  • TypeScript Course is a fantastic free email course from Joe Previte (@jsjoeio). It's a to learn TypeScript a little bit at a time.
  • TypeScript Deep Dive is just what it sounds like - a thorough deep dive into TypeScript. It's a free book, with an available paid course on Udemy for those who want more .
  • TypeScript Handbook is the official TypeScript documentation. For better or worse, I don't think it's a great place to start, but it contains every detail you might possibly need. I've found myself coming back to it from time to time to look up specific features and to help debug this-and-that.
*It's a flower from a plum tree. Like it?*

SHIP PRODUCTS
THAT MATTER

💌 Tiny Improvements: my weekly newsletter sharing one small yet impactful idea for product builders, startup founders, and indiehackers.

It's your cheat code for building products your customers will love. Learn from the CTO of a Y Combinator-backed startup, with past experience at Google, Stripe, and Microsoft.

    Join the other product builders, and start shipping today!