Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript with NextJS

I've been struggling to integrate Typescript with NextJS, mainly with destructured parameters in getInitialProps but also with the type of page functions. For example, here is my _app.tsx:

import { ThemeProvider } from 'styled-components';
import App, { Container } from 'next/app';
import Cookie from 'js-cookie';
import React from 'react';

import { CookiesProvider } from '../components/CookiesContext';
import THEME from '../styles/config';

import {
  GlobalStyles,
  Wrapper,
} from './_app.style';

type Props = {
  cookieSettings: {
    functionalOn: string;
    performanceOn: string;
  }
};

class Page extends App<Props> {
  static async getInitialProps({
    Component,
    ctx,
  }) {
    // Get initial page props
    const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};

    // Get cookies settings
    const cookieSettings = {
      functionalOn: process.browser ? Cookie.get('functionalCookies') : ctx.req.cookies.functionalCookies,
      performanceOn: process.browser ? Cookie.get('performanceCookies') : ctx.req.cookies.performanceCookies,
    };

    return {
      cookieSettings,
      pageProps,
    };
  }

  render() {
    const {
      Component,
      cookieSettings,
      pageProps,
    } = this.props;

    return (
      <Container>
        <ThemeProvider theme={THEME}>
          <CookiesProvider {...cookieSettings}>
            <Wrapper>
              <GlobalStyles />
              <Component {...pageProps} />
            </Wrapper>
          </CookiesProvider>
        </ThemeProvider>
      </Container>
    );
  }
}

export default Page;

The only error I get here is that the Component and ctx parameters are implicitly of 'any' type. I'm using @types/next, as well as v8.1.1.Canary.40 of NextJS which includes types, so I thought stuff like this would be covered but I seem to have to declare what they are myself and I don't know since it's a third party package.

Also, I have a page file like this:

import { NextPage } from 'next';
import React, { Fragment } from 'react';

import Metadata from '../components/Metadata';
import withPageError from '../components/PageError/withPageError';

type Props = {
  // TODO: Add props
};

const getInitialProps = async ({ res }) => {
  const page = await getPageData();
  if (!page && res) res.statusCode = 404;
  if (!page) return {};
  return { page };
};

const Page: NextPage<Props> = () => (
  <Fragment>
    <Metadata
      pageDescription="Page"
      pageTitle="Page"
      socialDescription="Page"
      socialTitle="Page"
    />
  </Fragment>
);

Page.getInitialProps = getInitialProps;

export default withPageError(Page);

Here I get red squigglies on Page.getInitialProps with the following error:

Type '({ res }: { res: any; }) => Promise<{ page?: undefined; } | { page: any; }>' is not assignable to type '(ctx: NextPageContext) => Promise<Props>'.

Also an error on const Page which is huge and doesn't make any sense to me.

What am I missing in all of this? Surely I don't have to redeclare types for a load of NextJS stuff?

like image 677
CaribouCode Avatar asked May 29 '19 15:05

CaribouCode


1 Answers

You just need to type getInitialProps, like that:

import React from 'react'
import { NextPageContext } from 'next'

interface Props {
  userAgent?: string;
}

export default class Page extends React.Component<Props> {
  static async getInitialProps({ req }: NextPageContext) {
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
    return { userAgent }
  }

  render() {
    const { userAgent } = this.props
    return <main>Your user agent: {userAgent}</main>
  }
}

Next.js docs - https://nextjs.org/docs/api-reference/data-fetching/getInitialProps

like image 117
beeinger Avatar answered Dec 07 '22 23:12

beeinger