next.js icon indicating copy to clipboard operation
next.js copied to clipboard

InferGetServerSidePropsType doesn't work with conditional returns

Open anthonyalayo opened this issue 3 years ago • 10 comments

Verify canary release

  • [X] I verified that the issue exists in Next.js canary release

Provide environment information

❯ next info

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 20.6.0: Tue Feb 22 21:10:41 PST 2022; root:xnu-7195.141.26~1/RELEASE_X86_64
Binaries:
  Node: 17.9.0
  npm: 8.5.5
  Yarn: N/A
  pnpm: N/A
Relevant packages:
  next: 12.1.6-canary.16
  react: 18.0.0
  react-dom: 18.0.0

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Describe the Bug

In the documentation here: https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#redirect

We are shown that we can return a redirect or a notFound instead of props. I have attempted to do that in TypeScript here:

export default function user({ user }: InferGetServerSidePropsType<typeof getServerSideProps>) {
    return (
        <div>{user}</div>
    );
}

export const getServerSideProps = async ({ req }: GetServerSidePropsContext) => {
    const userJwt = parseCookies({ req })['jwt']
    if (!userJwt) {
        return { redirect: { destination: '/login', permanent: false } }
    }

    const user = await prisma.users.findFirst()
    return { props: { user } }
}

If there is logic handling a conditional redirect, we don't get type inference. Here is a WebStorm screenshot showing that: image

Once I remove the conditional logic:

export default function user({ user }: InferGetServerSidePropsType<typeof getServerSideProps>) {
    return (
        <div>{user?.email}</div>
    );
}

export const getServerSideProps = async ({ req }: GetServerSidePropsContext) => {
    const user = await prisma.users.findFirst()
    return { props: { user } }
}

Then type inference works again: image

Expected Behavior

Type inference works with conditionals.

To Reproduce

One way of reproducing is copy/pasting what I provided in the bug description:

export default function user({ user }: InferGetServerSidePropsType<typeof getServerSideProps>) {
    return (
        <div>{user}</div>
    );
}

export const getServerSideProps = async ({ req }: GetServerSidePropsContext) => {
    const userJwt = parseCookies({ req })['jwt']
    if (!userJwt) {
        return { redirect: { destination: '/login', permanent: false } }
    }

    const user = await prisma.users.findFirst()
    return { props: { user } }
}

This however requires extra dependencies to be installed. With no dependencies, you can simply take the example from the docs and attempt to use type inference with it: https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#redirect

anthonyalayo avatar May 02 '22 08:05 anthonyalayo

You have to use GetServerSideProps/GetStaticProps interface instead and pass the Props interface of your page component to its first generic for the props to be picked up by Infer. You will have to do it anyway once you need to have some sort of baseline props interface across all pages with added benefit the return type of your SSR function will also be typechecked.

GabenGar avatar May 03 '22 18:05 GabenGar

@GabenGar I just switched getServerSideProps to:

export const getServerSideProps: GetServerSideProps = async ({ req }) => {
    const userJwt = parseCookies({ req })['jwt']
    if (!userJwt) {
        return { redirect: { destination: '/login', permanent: false } }
    }

    const user = await prisma.users.findFirst()
    return { props: { user } }
}

and user is still being detected as any. Could you copy/paste the correct way of writing it? Perhaps I'm missing something?

anthonyalayo avatar May 03 '22 18:05 anthonyalayo

Let's say you have interface PageProps { user: unknown } declared somewhere on the component file. Then you write GetServerSideProps<PageProps> as the type for your getServerSideProps() function.

GabenGar avatar May 03 '22 19:05 GabenGar

I think I attempted what you mentioned, but no luck still. I also went back to the docs: https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#getserversideprops-with-typescript

and I couldn't find a similar example either. I'm still new to TypeScript, perhaps you could copy/paste my example with the changes?

anthonyalayo avatar May 03 '22 19:05 anthonyalayo

I have a page which uses it. The lines of interest are 30 and 128.

GabenGar avatar May 03 '22 19:05 GabenGar

That worked, thank you @GabenGar! Here it is completely, so people can reference it in the future:

import { prisma } from '../db'
import { users } from '@prisma/client'
import { GetServerSideProps, InferGetServerSidePropsType } from "next/types";
import { parseCookies } from "nookies";

export default function Users({ users }: InferGetServerSidePropsType<typeof getServerSideProps>) {
    return (
        <ul>
            {users.map(user => (
                <li key={user.id}>{user.email}</li>
            ))}
        </ul>
    );
}

interface IProps {
    users: users[]
}

export const getServerSideProps: GetServerSideProps<IProps> = async ({ req }) => {
    const userJwt = parseCookies({ req })['jwt']
    if (!userJwt) {
        return { redirect: { destination: '/login', permanent: false } }
    }

    const users = await prisma.users.findMany()
    return { props: { users } }
}

My mistake was that I did this:

interface IProps {
    props: {
        users: users[]
    }
}

@GabenGar, aren't we returning an object with everything inside a props object? Why does including that in the interface not work? I'm confused about that.

anthonyalayo avatar May 03 '22 19:05 anthonyalayo

aren't we returning an object with everything inside a props object? Why does including that in the interface not work? I'm confused about that.

Because the generic for GetServerSideProps interface defines the shape of the props object, not the entire result from it.

GabenGar avatar May 03 '22 20:05 GabenGar

Thanks @GabenGar, that makes sense. I appreciate the help!

@balazsorban44, could this ticket track making the documentation here more clear? https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#getserversideprops-with-typescript

I'm not a TypeScript expert, but I could imagine others getting confused by it as well.

anthonyalayo avatar May 03 '22 20:05 anthonyalayo

@balazsorban44 bumping this issue

anthonyalayo avatar Jul 02 '22 19:07 anthonyalayo

I'm not a TypeScript expert, but I could imagine others getting confused by it as well.

@anthonyalayo you're absolute right! I was so confused on why this did not work.

Updating the documentation would certainly help other people.

Haberkamp avatar Aug 05 '22 18:08 Haberkamp

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.

github-actions[bot] avatar Oct 21 '22 00:10 github-actions[bot]