TypeScript Partial Utility Type

📝 Table Of Contents

☕️ Summary

The Partial<Type> utility type constructs a type with all properties of Type set to optional. It returns a type that represents all subsets of the given type. This creates a really flexible way of making a new copy of an updated object such as a user. With Partial we can re-use some or all properties from Types that already exist.

Fun fact: Partial is used in the FunctionComponent interface for the `React.FC` type for creating functional components.

In this guide, we're going to update information about a user by using the Partial Type and display the different versions of the user for each update.

Note: This example is an extension of the official TypeScript docs section on the Partial utility type. We'll use the same update function from the docs.

▶️ Code Sandbox Example

🛠 How It Works

In this example, we create some UserCard components to render some information about each version of the user each time we update the user with new properties.

In User.tsx, we create an interface User to define the types of data that a user object can have.

// User.tsx

interface User {
  firstName: string;
  lastName: string;
  age: number;
  favoriteSongs: string[];
}

We need a function to update the user given a user object that already exists of Type User, and fields to update the user of a newly constructed Type Partial<User>. This newly constructed Type represents all subsets of the User Type. This means that we can update the user by modifying 1, 2, 3... or ALL of the properties that make up the User Type.

We're also going to export this function so that we can use it in our App.tsx.

// User.tsx

export function updateUser(user: User, fieldsToUpdate: Partial<User>) {
  return { ...user, ...fieldsToUpdate };
}

Also, even though we have made all properties of the User Type required, the new Partial<User> Type sets all properties to optional for us - allowing us to accept any combination of User interface fields to update. Very handy!

The last thing we have for our User.tsx file is a UserCard component that just handles some rendering for us. We also export this component so that we can use it in App.tsx.

Note: keys of react elements should be done in a better way, but for the sake of this guide we're just using some string variables combined with a randomly generated number so that keys are all unique. Works for now. 😇

// User.tsx

export function UserCard(user: User) {
  return (
    <div>
      <ul>
        <li>
          <strong>First</strong> {user.firstName}
        </li>
        <li>
          <strong>Last</strong> {user.lastName}
        </li>
        <li>
          <strong>Age</strong> {user.age}
        </li>
        <strong>Favorite Songs</strong>
        {user.favoriteSongs.map((song) => {
          return (
            // don't use keys like this, you would instead want a unique id
            <li key={song + Math.random()}>
              <strong>
                {"#" + (user.favoriteSongs.indexOf(song) + 1).toString()}
              </strong>{" "}
              {song}
            </li>
          );
        })}
      </ul>
    </div>
  );
}

We also need to make a style sheet to import into App.tsx. This will do.

/* styles.css */

.App {
  font-family: sans-serif;
}

.App h1 {
  text-align: center;
}

.user-card ul {
  list-style: none;
  display: block;
}

.user-card div {
  border-style: solid;
  margin: 1rem;
}

Now we can import our style sheet and modules that we've exported from User.tsx.

Here is where we're going to create some user objects using our updateUser function.

// App.tsx

import "./styles.css";
import { UserCard, updateUser } from "./User";

const user1 = {
  firstName: "Vitalik",
  lastName: "Buterin",
  age: 27,
  favoriteSongs: [
    "Hold On - Moxura",
    "Pressure - Martin Garrix",
    "Run It Up - Lil Tjay",
  ],
};

const user2 = updateUser(user1, {
  firstName: "Satoshi",
  favoriteSongs: ["Happier - Bastille"],
});

const user3 = updateUser(user2, { lastName: "Nakamoto", age: 900 });

const listOfUsers = [user1, user2, user3];

You can see that user2 takes user1 as it's first argument and the second argument is a new subset of the User Type with only the firstName and favoriteSongs properties.

We do the same thing for user3 but passing in user2 as the first argument. This will return us a new user with the current fields in user2 and the new updated fields.

Lastly, we export the App and just map out the list of users into the UserCard component. ✅

// App.tsx

export default function App() {
  return (
    <div className="App">
      <h1>*Partial* Utility Type 🛠</h1>
      {listOfUsers &&
        listOfUsers.map((user) => {
          return (
            <div className="user-card" key={listOfUsers.indexOf(user)}>
              <h3>Version: {listOfUsers.indexOf(user) + 1}</h3>
              <UserCard
                firstName={user.firstName}
                lastName={user.lastName}
                age={user.age}
                favoriteSongs={user.favoriteSongs}
              ></UserCard>
            </div>
          );
        })}
    </div>
  );
}

🧠 Quiz Question

The new type constructed by Partial<Type> sets all properties of Type to:

Test your knowledge 🧠