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