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:
1import Footer from '@components/Footer';2import Header from '@components/Header';34const PageLayout = ({ children }) => {5return (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};1516export 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:
1import Footer from '@components/Footer';2import Header from '@components/Header';34const PageLayout = ({ children }) => {5return (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};1516export default PageLayout;
Next, I'll add a type annotation to the children prop:
1import Footer from '@components/Footer';2import Header from '@components/Header';34const PageLayout = ({ children }: { children: React.ReactNode }) => {5return (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:
1import Footer from '@components/Footer';2import Header from '@components/Header';34const PageLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {5return (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:
1import Footer from '@components/Footer';2import Header from '@components/Header';34const PageLayout: React.FC<{5children: React.ReactNode;6variant: 'dark' | 'light' | 'black' | 'tan';7}> = ({ children, variant }) => {8return (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:
1import Footer from '@components/Footer';2import Header from '@components/Header';34type PageLayoutProps = {5children: React.ReactNode;6variant: 'dark' | 'light' | 'black' | 'tan';7};89type PageLayout = React.FC<PageLayoutProps>;1011const PageLayout: PageLayout = ({ children, variant }) => {12return (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:
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:
1import Footer from "@components/Footer";2import Header from "@components/Header";34type PageLayoutProps = {5children: React.ReactNode;6variant: "dark" | "light" | "black" | "tan";7};89/**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 for12* the page content.13* @param {object} props14* @param {React.ReactNode} props.children - The page content15* @param {"dark" | "light" | "black" | "tan"} props.variant - The color variant for the page16* @returns {JSX.Element}17**/18const PageLayout: PageLayout = ({ children, title }) => {1920/* 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.