Skip to content

Commit

Permalink
Port 'with-static-export' example to TypeScript (#38268)
Browse files Browse the repository at this point in the history
The 'with-static-export' example is very useful but, if a developer wants to use TypeScript, converting it is nontrivial. There are some gotchas in the GetStaticProps/GetStaticPaths functions. This example should help jumpstart TS development for sites static generation.

## Documentation / Examples

- [x] Make sure the linting passes by running `pnpm lint`
- [x] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples)


Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
  • Loading branch information
shawncal and ijjk committed Aug 7, 2022
1 parent 3238352 commit 0d5a692
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 90 deletions.
@@ -1,6 +1,7 @@
import Link from 'next/link'
import { PostData } from '../types/postdata'

export default function Post({ title, body, id }) {
export default function Post({ title, body, id }: PostData) {
return (
<article>
<h2>{title}</h2>
Expand Down
17 changes: 17 additions & 0 deletions examples/with-static-export/lib/postdata_api.ts
@@ -0,0 +1,17 @@
import { PostData } from '../types/postdata'

export async function GetPost(id: string): Promise<PostData> {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${id}`
)
const postData: PostData = (await response.json()) as PostData
return postData
}

export async function GetPosts(): Promise<PostData[]> {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_page=1'
)
const postList: PostData[] = (await response.json()) as PostData[]
return postList
}
5 changes: 5 additions & 0 deletions examples/with-static-export/next-env.d.ts
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
23 changes: 16 additions & 7 deletions examples/with-static-export/package.json
@@ -1,17 +1,26 @@
{
"private": true,
"dependencies": {
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"serve": "11.2.0"
},
"scripts": {
"dev": "next",
"build": "next build",
"preexport": "npm run build",
"export": "next export",
"prestart": "npm run export",
"start": "serve out"
"start": "serve out",
"lint": "next lint"
},
"dependencies": {
"next": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"serve": "^14.0.1"
},
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"eslint": "8.19.0",
"eslint-config-next": "12.2.0",
"typescript": "^4.7.4"
}
}
34 changes: 0 additions & 34 deletions examples/with-static-export/pages/index.js

This file was deleted.

37 changes: 37 additions & 0 deletions examples/with-static-export/pages/index.tsx
@@ -0,0 +1,37 @@
import Head from 'next/head'
import { GetStaticProps, NextPage } from 'next'
import Post from '../components/post'
import { PostData, PostDataListProps } from '../types/postdata'
import { GetPosts } from '../lib/postdata_api'

export const getStaticProps: GetStaticProps = async (_context) => {
// fetch list of posts
const posts: PostData[] = await GetPosts()
return {
props: {
postDataList: posts,
},
}
}

const IndexPage: NextPage<PostDataListProps> = ({
postDataList,
}: PostDataListProps) => {
return (
<main>
<Head>
<title>Home page</title>
</Head>

<h1>List of posts</h1>

<section>
{postDataList.map((post: PostData) => (
<Post {...post} key={post.id} />
))}
</section>
</main>
)
}

export default IndexPage
48 changes: 0 additions & 48 deletions examples/with-static-export/pages/post/[id].js

This file was deleted.

57 changes: 57 additions & 0 deletions examples/with-static-export/pages/post/[id].tsx
@@ -0,0 +1,57 @@
import Link from 'next/link'
import Head from 'next/head'
import React from 'react'
import { GetStaticProps, GetStaticPaths, NextPage } from 'next'
import { ParsedUrlQuery } from 'querystring'
import { PostData, PostDataProps } from '../../types/postdata'
import { GetPosts, GetPost } from '../../lib/postdata_api'

interface Params extends ParsedUrlQuery {
id: string
}

export const getStaticPaths: GetStaticPaths<Params> = async () => {
const postList: PostData[] = await GetPosts()
return {
paths: postList.map((post) => {
return {
params: {
id: post.id.toString(),
},
}
}),
fallback: false,
}
}

export const getStaticProps: GetStaticProps<PostDataProps, Params> = async (
context
) => {
const { id } = context.params! as Params
const postData: PostData = await GetPost(id)
return {
props: {
postData,
},
}
}

const Post: NextPage<PostDataProps> = ({ postData }: PostDataProps) => {
return (
<main>
<Head>
<title>{postData.title}</title>
</Head>

<h1>{postData.title}</h1>

<p>{postData.body}</p>

<Link href="/">
<a>Go back to home</a>
</Link>
</main>
)
}

export default Post
20 changes: 20 additions & 0 deletions examples/with-static-export/tsconfig.json
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"moduleResolution": "node"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
14 changes: 14 additions & 0 deletions examples/with-static-export/types/postdata.ts
@@ -0,0 +1,14 @@
export interface PostData {
userId: number
id: number
title: string
body: string
}

export interface PostDataProps {
postData: PostData
}

export interface PostDataListProps {
postDataList: PostData[]
}

0 comments on commit 0d5a692

Please sign in to comment.