-
Notifications
You must be signed in to change notification settings - Fork 27.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infer Types leading to props: never
#15913
Comments
props: never
Next only checks this: export type InferGetServerSidePropsType<T> = T extends GetServerSideProps<
infer P,
any
>
? P
: T extends (
context?: GetServerSidePropsContext<any>
) => Promise<GetServerSidePropsResult<infer P>>
? P
: never So if neither the function is typed nor the argument, I think it has no way of knowing if the function is actually of type export type InferGetServerSidePropsType<T> = T extends GetServerSideProps<
infer P,
any
>
? P
: T extends (
context?: any
) => Promise<GetServerSidePropsResult<infer P>>
? P
: never would make it infer your type, because the conditon would hold true? |
I think you're right, however this one works, where the function isn't typed, and nor is the argument: export const getServerSideProps = async (ctx) => { |
It also happens in cases like this: export const getServerSideProps = async (ctx) => {
const { userId } = ctx.params
const user = await getUser(userId)
if(user) {
return {
props: { user }
}
} else {
return { notFound: true }
}
}; |
This also doesn't work: export const getServerSideProps: GetServerSideProps = async ({ params }) => {
return {
props: { foo: "bar" }
}
};
export const Page = (props: InferGetServerSidePropsType<typeof getServerSideProps>) => { ... } or export const getServerSideProps: GetServerSideProps = async ({ params }: GetServerSidePropsContext) => {
return {
props: { foo: "bar" }
}
};
export const Page = (props: InferGetServerSidePropsType<typeof getServerSideProps>) => { ... } But removing the export const getServerSideProps = async ({ params }: GetServerSidePropsContext) => {
return {
props: { foo: "bar" }
}
};
export const Page = (props: InferGetServerSidePropsType<typeof getServerSideProps>) => { ... } NextJS: For me, the magic was to make sure the |
I can confirm that @tgallacher bottom sample also works for me. |
This is a sneaky one, my string props were mistyped but that didn't even cause a problem with strict TS. It only was an issue when I had an object in the props and wanted to access a property. |
Here is my solution, key was to provide return type of getServerSideProps into GetServerSideProps type, here is example: |
Setting types like this works:
|
I found out something interessing. return {
props: {
user: {
firstName,
lastName
}
}
}; If i additionally return a redirect or a notFound conditionally type inference stops working for me. return {
redirect: {
destination: '',
permanent: false,
},
}; return {
notFound: true
};
This also works for me, but it would be more comfortable if it would work without the extra type specification. |
It probably works in first case because return type is |
Having the same issue. Is there a proper solution? Not quite sure which one to choose in all the previous suggestions. |
The easiest solution imo would be by adding a PageProps type to type PageProps = {
user: {
firstName: string,
lastName: string
};
};
export const getServerSideProps: GetServerSideProps<PageProps> = async (ctx) => {
return {
props: {
user: {
firstName,
lastName
}
}
};
};
export const Page = ({ user }: InferGetServerSidePropsType<typeof getServerSideProps>) => { ... } |
Hi @mkreuzmayr Thanks, sounds like an acceptable solution! However it doesn't work with import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import { AuthAction, useAuthUser, withAuthUser, withAuthUserTokenSSR } from 'next-firebase-auth'
function Page(props: InferGetServerSidePropsType<typeof getServerSideProps>) {
const user = useAuthUser()
return (
<div>Message: {props.message} from {user.id}</div>
)
}
export default withAuthUser<InferGetServerSidePropsType<typeof getServerSideProps>>({
whenUnauthedBeforeInit: AuthAction.SHOW_LOADER,
whenUnauthedAfterInit: AuthAction.REDIRECT_TO_LOGIN
})(Page)
export const getServerSideProps: GetServerSideProps<PageProps> = withAuthUserTokenSSR({
whenUnauthed: AuthAction.REDIRECT_TO_LOGIN,
})(async () => {
return { props: { message: "Hello" } }
}) TS error:
|
I've found a workaround - first of all - something broke with Here's my hacky implementation that I just wrote:
|
Hey @KATT, thanks for your solution! You do not need to cast
|
I solved the issue by publishing my own infer type:
Installnpm install infer-next-props-type --save-dev Usage:getStaticPropsimport InferNextPropsType from 'infer-next-props-type'
export function getStaticProps() {
return {
props: { foo: 'bar' }
}
}
export default function Page(props: InferNextPropsType<typeof getStaticProps>) {
return ...
} getServerSidePropsimport InferNextPropsType from 'infer-next-props-type'
export function getServerSideProps() {
return {
props: { foo: 'bar' }
}
}
export default function Page(props: InferNextPropsType<typeof getServerSideProps>) {
return ...
} |
@HaNdTriX any reason for not updating the built-in type? |
Because my type still has some edge cases to cover. Will deprecate the module as soon as we found the perfect working type and push the changes upstream. |
Might be interesting microsoft/TypeScript#38511 |
returning an empty props in redirect worked for me: return {
redirect: {
destination: '',
permanent: false,
},
props: {}
} |
Thanks @Markyiptw for the hint, that helped me a lot in figuring out what was going on. The disadvantage of that solution is that the props will all become optional, so that may not always be ideal. I slapped together this custom type that seems to do work well when using export type CustomInferGetServerSidePropsType<T> = T extends (
context?: any
) => Promise<{ props: infer P }>
? P
: T extends (context?: any) => Promise<GetServerSidePropsResult<infer P>>
? P
: never; I'm sharing this because the other types shared in this issue didn't quite work for my cases. |
I found a best work around. Then the Problem was as said here #32434 (comment). So all after adding the |
Looks like this will hopefully be solved with Typescript 4.9! |
I was playing around with the new satisfies keyword and I can certainly see it helping. We’ll still need to cast the notFound and redirects returns as consts. I’m also of the opinion that we should expect props to be able to return null unless a type guard is in place, which I’ve also included in the below example. |
I have created a PR to solve this issue^^ |
## 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>
This issue has been fixed by #40635 |
Closing per above |
This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Describe the bug
causes
props: never
however the following works fine:
as does:
The text was updated successfully, but these errors were encountered: