mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
## 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 <[email protected]>
53 lines
1.2 KiB
TypeScript
53 lines
1.2 KiB
TypeScript
import type { InferGetStaticPropsType, GetStaticPropsContext } from 'next'
|
|
import { expectTypeOf } from 'expect-type'
|
|
|
|
describe('InferGetServerSidePropsType', () => {
|
|
it('should work with sync functions', async () => {
|
|
function getStaticProps(context: GetStaticPropsContext) {
|
|
if (context.params?.notFound) {
|
|
return {
|
|
notFound: true,
|
|
}
|
|
}
|
|
|
|
return {
|
|
props: {
|
|
foo: 'bar',
|
|
},
|
|
}
|
|
}
|
|
|
|
type PageProps = InferGetStaticPropsType<typeof getStaticProps>
|
|
|
|
expectTypeOf<PageProps>().toEqualTypeOf<{ foo: string }>()
|
|
})
|
|
|
|
it('should work with async functions', async () => {
|
|
async function getStaticProps(context: GetStaticPropsContext) {
|
|
if (context.params?.notFound) {
|
|
return {
|
|
notFound: true,
|
|
}
|
|
}
|
|
|
|
if (context.params?.redirect) {
|
|
return {
|
|
redirect: {
|
|
destination: '/',
|
|
},
|
|
}
|
|
}
|
|
|
|
return {
|
|
props: {
|
|
foo: 'bar',
|
|
},
|
|
}
|
|
}
|
|
|
|
type PageProps = InferGetStaticPropsType<typeof getStaticProps>
|
|
|
|
expectTypeOf<PageProps>().toEqualTypeOf<{ foo: string }>()
|
|
})
|
|
})
|