Jay

Jay

Adopting TypeScript in the nextjs-blog project

Following the guidelines recommended by Thomas J. Smith (https://www.linkedin.com/pulse/startup-tech-lessons-thomas-smith), I wanted to convert this dummy project over to TypeScript. As the project changes over time, this should reduce the number of errors that make it out of dev.

Given that I already had a working project, it should not surprisng that TypeScript adoption was easy. The directions provided by NextJS.org, however, were underwhelming for two reasons:

  • Types controlled by the implementation of lib/posts.tsx were spelled out in client code rather than exported
  • My project had been refactored, which the NextJS.org instructions did not accommodate
  • I found a potential bug in the tutorial

Better types through export

As an example of exporting types, rather than allowing getPostData to return a dictionary, I explicitly exported it as:

interface PostMatterData { date: string; title: string }
export interface AllPostData extends PostMatterData { id: string }
function getPostData(id: string): AllPostData { /* ... */ }
export function getSortedPostsData() { /* return an array populated by calls to getPostData */ }

This allowed me to have a clearer client:

export default function Home({
  allPostsData
}: {
  allPostsData: AllPostData[]
}) { /* ... */ }

Rather than the suggested:

export default function Home({
  allPostsData
}: {
  allPostsData: {
    date: string
    title: string
    id: string
  }[]
}) { /* ... */ }

It would be nice if Next.js was able to teach TypeScript about the relationship between the return of getStaticProps() and the call to the default exported function (Home(), in the case of this component) so that this wouldn't even be necessary.

Fancier Code Requires Generics

As for handling my refactored code, I had extended my Date component to pass along other props to the implementation. TypeScript needs to know something about the spread call, namely there may be other keys or maybe not, but we know there is one called dateString. Here's my complete method:

export default function Date<Props extends { dateString: string }>({
  dateString,
  ...otherProps
}: Props) {
  const date = parseISO(dateString)
  return <time dateTime={dateString} {...otherProps}>{format(date, 'LLLL d, yyyy')}</time>
}

A silly bug

The potential bug if found is rather interesting, here's the original code:

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const postData = await getPostDataWithHtml(params.id as string)
  /* ... */
}

Which yields:

Object is possibly 'undefined'.

Since the params property of the GetStaticPropsContext<Q> object passed as input to getStaticProps() is optional, we technically need to handle the cases where it's not present. Since I don't know when this could happen without diving into the Next.js documentation or possibly implementation, my current solution is to use the Non-null Assertion Operator: params!.id as string