getStaticProps not working well with TypeScript #16522
-
So I have this code: type NextGetStaticPropsCtx = {
params?: {
slug: string
}
preview?: boolean
previewDate?: any
}
export const getStaticProps: GetStaticProps = async (context: NextGetStaticPropsCtx) => {
const {
params: { slug },
} = context
...
} But I get 2 errors (red-squiggly lines): 1st one on
2nd one on
How do I solve it? |
Beta Was this translation helpful? Give feedback.
Replies: 18 comments 38 replies
-
Not sure if this is the correct way to do it but I found https://github.com/vercel/next.js/blob/master/examples/blog-starter-typescript/pages/posts/%5Bslug%5D.tsx#L58-L62 just now & did the following & it does not throw any errors now: type Params = {
params: {
slug: string
}
}
export const getStaticProps = async ({ params }: Params) => {
const { slug } = params
...
} |
Beta Was this translation helpful? Give feedback.
-
You can also use get export function getStaticProps(context: GetStaticPropsContext): GetStaticPropsResult<Params> {
// return { props : {} } as GetStaticPropsResult<Params>;
return { props: {} };
} |
Beta Was this translation helpful? Give feedback.
-
I'm new to next.js and TypeScript (in fact, I ended up here after following the tutorial) but I did as following: import { ParsedUrlQuery } from 'querystring';
type Props = {
post: PostData
}
interface Params extends ParsedUrlQuery {
id: string,
}
export const getStaticProps: GetStaticProps<Props, Params> = async (context) => {
const params = context.params! // ! is a non-null assertion
const post = await getPost(params.id)
return {
props: { post }
}
} Each field of EDIT:
|
Beta Was this translation helpful? Give feedback.
-
This worked without any linting error:
|
Beta Was this translation helpful? Give feedback.
-
This is how I managed to make it work with interface Params extends ParsedUrlQuery {
id: string,
}
export const getStaticProps: GetStaticProps = async (context) => {
const params = context.params as Params
const postData = await getPostData(params.id)
return {
props: {
postData
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Also don't forget to import ParsedUrlQuery to use the above solutions like I did. import { ParsedUrlQuery } from 'querystring'; |
Beta Was this translation helpful? Give feedback.
-
Based on the very helful answser this my version also working with import { ParsedUrlQuery } from 'querystring'
import { GetStaticPaths, GetStaticProps } from 'next'
// getPost(slug: string): Post | undefined
// getAllPostSlugs(): string[]
interface Params extends ParsedUrlQuery {
slug: string
}
type Props = {
post: NonNullable<ReturnType<typeof getPost>>
}
export const getStaticPaths: GetStaticPaths<Params> = async () => {
const paths = getAllPostSlugs().map(slug => ({ params: { slug } }))
return {
paths,
fallback: false
}
}
export const getStaticProps: GetStaticProps<Props, Params> = async ({ params }) => {
const post = getPost(String(params?.slug))
return post ? { props: { post } } : { notFound: true }
}
const Post: NextPage<Props> = ({ post }) => ... Updated based on Feedback from @rokinsky |
Beta Was this translation helpful? Give feedback.
-
This is mine that works for me.
|
Beta Was this translation helpful? Give feedback.
-
A marginally better type for I really tried to construct one which does but was having a hard time getting the props to be inferred correctly so unless someone can show me the light, this looks like the best that can be done for now. Since a import { GetStaticPropsResult, GetStaticPropsContext } from "next";
import { ParsedUrlQuery } from "querystring";
type PickRequired<T extends { [key: string]: any }, Keys extends keyof T> =
Omit<T, Keys> & { [K in Keys]-?: T[K] };
interface GetStaticPropsWithPath<
Q extends ParsedUrlQuery,
P extends { [key: string]: any } = { [key: string]: any },
> {
(context: PickRequired<GetStaticPropsContext<Q>, "params">): Promise<
GetStaticPropsResult<P>
>;
}
The interface PageParams extends ParsedUrlQuery {
slug: string;
}
interface PageProps {
title: string;
slug: string;
}
export const getStaticProps: GetStaticPropsWithPath<PageParams, PageProps> =
async (context) => {
return {
props: {
title: "My Page",
slug: context.params.slug, // params is now available by default
wrong: 32, // this will not throw an error, just like the original GetStaticProps
},
};
}; However, if you would like to enforce props returned by export const getStaticProps = async (
context: PickRequired<GetStaticPropsContext<PageParams>, "params">,
): Promise<GetStaticPropsResult<PageProps>> => {
return {
props: {
title: "My Page Title",
slug: context.params.slug, // params is now available by default
wrong: 42, // this will now throw an error
},
};
}; |
Beta Was this translation helpful? Give feedback.
-
@janluke's answer is wholesome and I think the right approach. Here's a version written out a bit further: import type { GetStaticPaths, GetStaticProps } from 'next';
import type { ParsedUrlQuery } from 'querystring';
interface PageProps {
pageData: string;
}
export default function Page({ pageData }: PageProps): JSX.Element {
return <div>{pageData}</div>;
}
interface StaticPathParams extends ParsedUrlQuery {
slug: string;
}
export const getStaticProps: GetStaticProps<PageProps, StaticPathParams> = async (context) => {
// The params being optional is super annoying.
const slug = context.params?.slug;
const pageData = await getPageData(slug);
return {
props: {
pageData,
},
};
};
export const getStaticPaths: GetStaticPaths<StaticPathParams> = async () => {
return {
paths: [...generate paths here...].map((slug) => ({
params: { slug },
})),
fallback: false,
};
}; A couple of reasons for this approach:
|
Beta Was this translation helpful? Give feedback.
-
Strict mode I didn't need to extend or manually assign types.
|
Beta Was this translation helpful? Give feedback.
-
Simple way if you want to use function declarations: import type { GetStaticPropsContext } from 'next'
export async function getStaticProps(context: GetStaticPropsContext<{ slug: string }>) {
const res = await someQueryFunction(API_URL, { slug: context.params?.slug })
return {
props: { res },
}
} |
Beta Was this translation helpful? Give feedback.
-
It looks like next.js typings are incorrect. |
Beta Was this translation helpful? Give feedback.
-
Note: please don't use my answer, it's may be wrong and definitely not tested. I'm leaving it here so I can find it when I revisit this for the 5th time in a year. That being said, feel free to use it: import * as React from 'react';
import type {
NextPage,
GetStaticPaths,
GetStaticPathsContext,
GetStaticPathsResult,
GetStaticProps,
GetStaticPropsResult,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next';
import { ParsedUrlQuery } from 'querystring';
type Post = {
userId: number;
id: number;
title: string;
body: string;
};
const getPosts = async (id: string): Promise<Post[]> => {
const posts = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${id}`);
return await posts.json();
};
interface Params extends ParsedUrlQuery {
id: string;
}
export const getStaticProps: GetStaticProps = async (
context: GetStaticPropsContext
): Promise<GetStaticPropsResult<{ posts: Post[] }>> => {
const { params } = context as { params: Params };
const { id } = params;
const posts = await getPosts(id);
if (!posts) return { notFound: true };
return { props: { posts }, revalidate: 1 };
};
export const getStaticPaths: GetStaticPaths = async (
// You don't need context unless you want to use locale and defaultLocale
context: GetStaticPathsContext
): Promise<GetStaticPathsResult> => {
const ids = ['1', '2', '3'];
const paths = ids.map(id => ({ params: { id } }));
return { paths, fallback: false };
};
type PostProps = InferGetStaticPropsType<typeof getStaticProps>;
const Post: NextPage = ({ posts }: PostProps) => {
return (
<main>
{posts.map((post: Post) => (
<div key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
))}
</main>
);
};
export default Post; |
Beta Was this translation helpful? Give feedback.
-
This was tricky, but this was what I got to work in Next.js 12.1.6. I don’t understand why previous examples above get away without using
|
Beta Was this translation helpful? Give feedback.
-
Simply use optional chaining and it works.
|
Beta Was this translation helpful? Give feedback.
I'm new to next.js and TypeScript (in fact, I ended up here after following the tutorial) but I did as following:
Each field of
context
is declared as optional, so you need to check for null or use a type assertion like I did forparams
.EDIT:
ParsedUrlQuery
getStaticProps
: use the …