Joel M. Turner

Illustration

Blog

Create an Avatar Component in Gatsby with TypeScript Part 3: Adding Types

This post is part of the Create an Avatar Component in Gatsby with TypeScript series

Part 1: We'll build the simple react componentPart 2: We'll start hooking up Gatsby imagePart 3: We'll type it with TypeScript

We left off with our avatar component working using Gatsby Image and still able to receive an image url. Now, let’s look at what it would take to type this component. I like to use type instead of interface for the props. You can read more about the difference between type and interface if you’d like.

The props type will look something like this:

1{...}
2type AvatarProps = {
3 url?: string;
4 altText?: string;
5 title?: string;
6 user?: "monster1" | "monster2";
7}
8
9function Avatar(props: AvatarProps) {
10{...}

The cool part here is that the user prop can be typed to match the graphql alias names. This helps anyone consuming this component know the values they can pass.

Let’s take a look at typing our data variable. We know the shape of what we expect because of our graphql. We just need to provide the correct typing at the childImageSharp level. Luckily Gatsby Image has a type of FixedObject that can help us out here. We pass the type to the static query hook like useStaticQuery<Data>(graphql to signify that we expect the return to be Data.

1{...}
2import Img, { FixedObject } from "gatsby-image"
3{...}
4
5type Data = {
6 monster1: {
7 childImageSharp: {
8 fixed: FixedObject;
9 };
10 };
11 monster2: {
12 childImageSharp: {
13 fixed: FixedObject;
14 };
15 };
16}
17
18function Avatar(props: AvatarProps) {
19 const data = useStaticQuery<Data>(graphql`
20{...}

Let’s refactor the redundancy in the Data type.

1type ChildImage = {
2 childImageSharp: {
3 fixed: FixedObject;
4 };
5}
6
7type Data = {
8 monster1: ChildImage;
9 monster2: ChildImage;
10}

Cool, now we should have something like this:

1import React from "react"
2import { useStaticQuery, graphql } from "gatsby"
3import Img, { FixedObject } from "gatsby-image"
4
5type AvatarProps = {
6 url?: string;
7 altText?: string;
8 title?: string;
9 user?: "monster1" | "monster2";
10}
11
12type ChildImage = {
13 childImageSharp: {
14 fixed: FixedObject;
15 };
16}
17
18type Data = {
19 monster1: ChildImage;
20 monster2: ChildImage;
21}
22
23function Avatar(props: AvatarProps) {
24 const data = useStaticQuery<Data>(graphql`
25 query {
26 monster1: file(relativePath: { eq: "monster-01-headshot.png" }) {
27 childImageSharp {
28 fixed(width: 75, height: 75) {
29 ...GatsbyImageSharpFixed
30 }
31 }
32 }
33 monster2: file(relativePath: { eq: "monster-02-headshot.png" }) {
34 childImageSharp {
35 fixed(width: 75, height: 75) {
36 ...GatsbyImageSharpFixed
37 }
38 }
39 }
40 }
41 `)
42
43 const { url, altText, title, user } = props
44 const styles = {
45 width: "75px",
46 height: "75px",
47 borderRadius: "50%",
48 }
49
50 if (url) {
51 return <img style={styles} src={url} alt={altText} title={title} />
52 }
53
54 return <Img style={styles} fixed={user && data[user].childImageSharp.fixed} alt={altText} title={title} />
55}
56
57export default Avatar

Thanks for following along!

Discuss this article on Twitter