Typescript Interesting Types

·

0 min read

Soil

This post was originally published at TK's blog.

These days I'm building a new project to understand some topics deeply. It is about user experience, web performance, accessibility, and a type system for consistent data.

This project I'm basically using React with Typescript. At first, I implemented a custom hook to handle the data fetching. One of the possible data types the fetch could return is a Product type. It looks like this:

type Product = {
  name: string;
  price: number;
  imageUrl: string;
  description: string;
  isShippingFree: boolean;
  discount: number;
};

Now that I could fetch some products, I wanted to use the list of products to render in the DOM. So I created a Product component. But as we are using Typescript, the props should be typed. In this case, I used the Product type. It looks like this:

export const Product = ({
  imageUrl,
  name,
  description,
  price,
  discount,
  isShippingFree,
}: ProductType) => (
  <Box>
    <Image imageUrl={imageUrl} imageAlt={name} />
    <TitleDescription name={name} description={description} />
    <Price price={price} discount={discount} />
    <Tag label="Free Shipping" isVisible={isShippingFree} />
  </Box>
);

And when I started implementing the Image component, I just passed the imageUrl and the imageAlt as props. It looks like this:

export const Image = ({ imageUrl }) =>
  <img src={imageUrl} />;

In this case, I couldn't use the Product type. But I could reuse it.

I learned about this new type: the Partial type. The idea of the Partial type is to build a new type based on the passed type and set all attributes to optional.

So, if we do a partial of the Product type, it would look like this:

type Product = {
  name?: string;
  price?: number;
  imageUrl?: string;
  description?: string;
  isShippingFree?: boolean;
  discount?: number;
};

All properties are set to optional.

And now I can use it for the Image component:

export const Image = ({ imageUrl }): Partial<ProductType> =>
  <img src={imageUrl} />;

But when I use the Image component, I can pass any props I want. I miss the type check. It doesn't break in compile time.

To fix that, I could just build an ImagePropsType and use it as the component props type.

type ImagePropsType = {
  imageUrl: string;
};

export const Image = ({ imageUrl }): ImagePropsType =>
  <img src={imageUrl} />;

But I already have the type for the imageUrl inside the Product type. So I started to search how I could reuse the type: I found the Pick type.

The Pick type lets me reuse the Product type by picking a set of properties I want:

type ImagePropsType = Pick<ProductType, 'imageUrl'>;

Now I ensure that type checking and the type reusability work well.

To build the whole Image component, I also needed to pass other props like: imageAlt and width.

What I wanted is the intersection of the Pick<ProductType, 'imageUrl'>, the imageAlt type, and the width type.

In Typescript, the representation of the intersection is the & operator.

I defined the ImageUrlType:

type ImageUrlType = Pick<ProductType, 'imageUrl'>;

And the ImageAttrType to represent both the imageAlt and the width:

type ImageAttrType = { imageAlt: string; width?: string };

And compose them together by insecting the types:

type ImagePropsType = ImageUrlType & ImageAttrType;

And the final result is:

import { ProductType } from 'types/Product';

type ImageUrlType = Pick<ProductType, 'imageUrl'>;
type ImageAttrType = { imageAlt: string; width?: string };
type ImagePropsType = ImageUrlType & ImageAttrType;

export const Image = ({ imageUrl, imageAlt, width }: ImagePropsType) =>
  <img src={imageUrl} alt={imageAlt} width={width} />;

I have the image URL, alt, width types intersected, and defined in the ImagePropsType. It can think of types as data and compose them. This is a very cool feature.

These are the interesting new types I learned this week.

My Twitter and Github.