3943b20f55
## Problem Currently the Next.js infer utility (`InferGetServerSidePropsType` and `InferGetStaticPropsType`) types can lead to a wrong inferred types (`never`). This happens if these functions return something different than: `{props: {}}`. **Example:** `getServerSideProps` ```typescript export async function getServerSideProps({ query }: GetServerSidePropsContext) { if (query.foo) { return { notFound: true, } } return { props: { foo: "bar" }, } } type PageProps = InferGetServerSidePropsType<typeof getServerSideProps> // => type PageProps = never ``` **Example:** `getStaticProps` ```typescript import type { InferGetStaticPropsType, GetStaticPropsContext } from 'next' export async function getStaticProps(context: GetStaticPropsContext) { if (context.params?.bar) { return { notFound: true, } } return { props: { foo: 'bar', }, } } type PageProps = InferGetStaticPropsType<typeof getStaticProps> // => type PageProps = never ``` This is because the first infer condition of the utility type is not satified leading to a never result. ```typescript export type InferGetServerSidePropsType<T> = T extends GetServerSideProps< infer P, // <- NOT SATISFIED any > ? P : T extends ( context?: GetServerSidePropsContext<any> ) => Promise<GetServerSidePropsResult<infer P>> ? P : never // <- NOT SATISFIED ``` ## Solution I have experimented with different solutions ending with a much simpler type, that is faster to execute, easier to read and universally usable for both prop variations. ```typescript /** * Flow: * - Make sure getStaticProps is a function * - Get its return type * - Extract the one that contains {props: any} * - Return the props */ export type InferGetStaticPropsType<T extends (args: any) => any> = Extract< Awaited<ReturnType<T>>, { props: any } >['props'] ``` ## Bug - [x] Related issues: fixes #36615, #15913, https://twitter.com/leeerob/status/1563540593003106306 - [x] Type tests added ## Future thoughts Since `InferGetStaticPropsType` and `InferGetServerSidePropsType` are now the same, it's api could be merged into one utility type (e.g: InferNextProps). I recommend doing this in a different PR. ## Additional info I have tested this approach using the following [external package](https://www.npmjs.com/package/infer-next-props-type) (@timneutkens sorry for the late PR). Since about 12 Month I haven't received any negative feedback (issues) regarding this approach. Co-authored-by: JJ Kasper <jj@jjsweb.site>
69 lines
1.5 KiB
TypeScript
69 lines
1.5 KiB
TypeScript
import type {
|
|
InferGetServerSidePropsType,
|
|
GetServerSidePropsContext,
|
|
} from 'next'
|
|
import { expectTypeOf } from 'expect-type'
|
|
|
|
describe('InferGetServerSidePropsType', () => {
|
|
it('should work with sync functions', async () => {
|
|
function getServerSideProps(context: GetServerSidePropsContext) {
|
|
if (context.params?.notFound) {
|
|
return {
|
|
notFound: true,
|
|
}
|
|
}
|
|
|
|
return {
|
|
props: {
|
|
foo: 'bar',
|
|
},
|
|
}
|
|
}
|
|
|
|
type PageProps = InferGetServerSidePropsType<typeof getServerSideProps>
|
|
|
|
expectTypeOf<PageProps>().toEqualTypeOf<{ foo: string }>()
|
|
})
|
|
|
|
it('should work with async functions', async () => {
|
|
async function getServerSideProps(context: GetServerSidePropsContext) {
|
|
if (context.params?.notFound) {
|
|
return {
|
|
notFound: true,
|
|
}
|
|
}
|
|
|
|
if (context.params?.redirect) {
|
|
return {
|
|
redirect: {
|
|
destination: '/',
|
|
},
|
|
}
|
|
}
|
|
|
|
return {
|
|
props: {
|
|
foo: 'bar',
|
|
},
|
|
}
|
|
}
|
|
|
|
type PageProps = InferGetServerSidePropsType<typeof getServerSideProps>
|
|
|
|
expectTypeOf<PageProps>().toEqualTypeOf<{ foo: string }>()
|
|
})
|
|
|
|
it('should work with promised props', async () => {
|
|
async function getServerSideProps() {
|
|
return {
|
|
props: Promise.resolve({
|
|
foo: 'bar',
|
|
}),
|
|
}
|
|
}
|
|
|
|
type PageProps = InferGetServerSidePropsType<typeof getServerSideProps>
|
|
|
|
expectTypeOf<PageProps>().toEqualTypeOf<{ foo: string }>()
|
|
})
|
|
})
|