Typescript Interesting Types
Photo by Nijwam SwargiaryThese 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.