9/18 | Validate feature ideas earlier with AI-driven prototypes

What are best AI tools? Take the State of AI survey

Builder.io
Builder.io
Contact sales

9/18 | Validate feature ideas earlier with AI-driven prototypes

What are best AI tools? Take the State of AI survey

Builder.io
Builder.io
< Back to blog

TypeScript

TypeScript Utility Types You Need to Know

February 3, 2023

Written By Steve Sewell

Are you ever building something in TypeScript and realize...

Screen Shot 2023-02-03 at 12.48.22 PM.png

AGH! This package is not exporting a type I need!

Fortunately, TypeScript gives us a number of utility types that can solve this common problem.

For instance, to grab the type returned from a function, we can use the ReturnType utility:

But we have one little problem. getContent is an async function that returns a promise, so currently our Content type is actuallyPromise, which is not what we want.

For that, we can use the Awaited type to unwrap the promise and get the type of what the promise resolves to:

Now we have exactly the type we needed, even though it is not explicitly exported. Well, that’s a relief.

But what if we need argument types for that function?

For instance, getContent takes an optional argument called ContentKind that is a union of strings. I really don’t want to have to type this out manually, so let’s use the Parameters utility type to extract its parameters:

Parameters gives you a tuple of the argument types, and you can pull out a specific parameter type by index like so:

But we have one last issue. Because this is an optional argument, our ContentKind type right now is actually ContentKind | undefined, which is not what we want.

For this, we can use the NonNullable utility type, to exclude any null or undefined values from a union type.

Now our ContentKind type perfectly matches the ContentKind in this package that was not being exported, and we can use it in our processContent function like so:

Utility Types with React

Utility types can also help us a lot with our React components.

For instance, below I have a simplistic component to edit calendar events, where we maintain an event object in state and modify the event title on change.

Can you spot the state bug in this code?

Doh, we are mutating the event object directly.

This will cause our input to not work as expected because React will not be aware of the state change and subsequently not rerender.

What we need to be doing is calling setEvent with a new object.

But wait, why didn’t TypeScript catch that?

Well, technically you can mutate objects with useState. You just basically never should. We can improve our type safety here by using the Readonly utility type, to enforce that we should not be mutating any properties on this object:

Now our prior bug will be caught for us automatically, woo!

Now, when we update our code to copy the event as needed, TypeScript is happy again:

But, there is still a problem with this. Readonly only applies to top level properties of the object. We can still mutate nested properties and arrays without errors:

But, now that we are aware of Readonly, we can combine that with its sibling ArrayReadonly, and a little bit of magic, and make our own DeepReadonly type like so:

Thanks to Dean Merchant for the above code snippet.

Now, using DeepReadonly, we cannot mutate anything in the entire tree, preventing a whole range of bugs that could occur.

Which will only pass type check if treated properly immutably:

One additional pattern you may want to use for this kind of complexity is to move this logic to a custom hook, which we can do like so:

This allows us to simply provide the properties that have changed and the copying can be managed automatically for a nice DX and safety guarantees.

But we have a new problem. updateEvent expects the full event object, but what we intend is to only have a partial object, so we get the following error:

Fortunately, this is easily solved with the Partial utility type, which makes all properties optional:

Alongside Partial, it’s worth also knowing the Required utility type, which does the opposite - takes any optional properties on an object and makes them all required.

Or, if we only want certain keys to be allowed to be included in our updateEvent function, we could use the Pick utility type to specify the allowed keys with a union:

Or similarly, we can use Omit to omit specified keys:

Moar utilities

We touched on a good bit of typescript utilities here! But here is a quick overview of the remaining ones, which are all pretty useful in my opinion.

Record<KeyType, ValueType>

Easy way to create a type representing an object with arbitrary keys that have a value of a given type:

Exclude<UnionType, ExcludedMembers>

Removes all members from a union that are assignable to the ExcludeMembers type.

Extract<Union, Type>

Removes all members from a union that are not assignable to Type.

Just like Parameters, but for constructors:

Gives you the instance type of a constructor.

Gives you the type of the this parameter for a function, or unknown if none is provided.

Removes the this parameter from a function type.

Conclusion

Utility types in TypesScript are useful. Use them.

About me

Hi! I'm Steve, CEO of Builder.io.

We make a way to drag + drop with your components to create pages and other CMS content on your site or app, visually.

You may find it interesting or useful:

Share

Twitter
LinkedIn
Facebook
Share this blog
Copy icon
Twitter "X" icon
LinkedIn icon
Facebook icon

Visually edit your codebase with AI

Using simple prompts or Figma-like controls.

Try it nowGet a demo

Design to Code Automation

A pragmatic guide for engineering leaders and development teams


Continue Reading
design9 MIN
How to generate (actually good) designs with AI
September 17, 2025
AI9 MIN
7 Levels of Context Engineering for Designers
September 16, 2025
Design to Code8 MIN
Git Branching for Designers
September 11, 2025

Product

Visual CMS

Theme Studio for Shopify

Sign up

Login

Featured Integrations

React

Angular

Next.js

Gatsby

Resources

User Guides

Developer Docs

Forum

Blog

Github

Get In Touch

Chat With Us

Twitter

Linkedin

Careers

© 2020 Builder.io, Inc.

Security

Privacy Policy

Terms of Service

Get the latest from Builder.io

By submitting, you agree to our Privacy Policy

  • Fusion

  • Publish

  • Product Updates

  • Design to Code

  • Headless CMS

    Multi-Brand CMS

  • Landing Pages

  • Web Apps

  • Prototypes

  • Marketing Sites

  • Headless Commerce

  • Documentation

  • Fusion Docs

  • Publish Docs

  • Blog

  • Webinars

  • Guides

  • Case Studies

  • Community Forum

  • Partners

  • Affiliate Program

  • CMS Integrations

  • CMS Blueprints

  • Glossary

  • Figma to Code Guide

  • Headless CMS Guide

  • Headless Commerce Guide

  • Composable DXP Guide

  • About

  • Careers

  • Contact Sales

Security

Privacy Policy

SaaS Terms

Compliance

Cookie Preferences

YouTube icon
Github icon
Blsky Icon
Twitter "X" icon
LinkedIn icon
Feed Icon
Gartner Cool Vendor 2024