rsnext/test/unit/infer-get-server-side-props-type.test.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

70 lines
1.5 KiB
TypeScript
Raw Normal View History

fix: InferGetServerSidePropsType and InferGetStaticPropsType (#40635) ## 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>
2022-09-21 00:25:01 +02:00
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 }>()
})
})