在 Next.js 中使用 getInitialProps 和 TypeScript

Using getInitialProps in Next.js with TypeScript

从文档、Next.js 5.0 公告和互联网上的各种文章来看,Next.js 似乎很好地支持 TypeScript,而且很多人都在使用它。

但是这些线程表明 getInitialProps 对 Next.js 应用至关重要,但它不起作用:

我该如何解决?在 class 和功能组件中,当我执行 ComponentName.getInitialProps = async function() {...} 时,出现以下错误:

[ts] Property 'getInitialProps' does not exist on type '({ data }: { data: any; }) => Element'.

编辑:此答案自 Next 9 发布以来已过时。请参阅上面的答案。

解决问题的经典方法是将getInitialProps声明为静态成员:

class MyComponent extends React.Component<{...}, {}> {

  static async getInitialProps(ctx: any) {
    return {...}
  }

  render() {...}

}

使用无状态组件时,您可以声明 React.SFC 的简单扩展:

interface StatelessPage<P = {}> extends React.SFC<P> {
  getInitialProps?: (ctx: any) => Promise<P>
}

const MyComponent: StatelessPage<{...}> = (...) => ...

MyComponent.getInitialProps = async (ctx) => {...}

Next.js 的类型在具有新版本 7.0.6 的 DefinitelyTyped project 中维护。

为了使用新类型,请确保将它们导入到您的项目中:

npm install --save-dev @types/next@7.0.6

以下是为无状态功能组件键入 getInitialProps 的方式:

import { NextFunctionComponent, NextContext } from 'next'

// Define what an individual item looks like
interface IDataObject {
  id: number,
  name: string
}

// Define the props that getInitialProps will inject into the component
interface IListComponentProps {
  items: IDataObject[]
}

const List: NextFunctionComponent<IListComponentProps> = ({ items }) => (
  <ul>
    {items.map((item) => (
      <li key={item.id}>
        {item.id} -- {item.name}
      </li>
    ))}
  </ul>
)

List.getInitialProps = async ({ pathname }: NextContext) => {
  const dataArray: IDataObject[] =
    [{ id: 101, name: 'larry' }, { id: 102, name: 'sam' }, { id: 103, name: 'jill' }, { id: 104, name: pathname }]

  return { items: dataArray }
}

export default List

以下是输入 getInitialProps 以获得 class 的方法:

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

// Define what an individual item looks like
interface IDataObject {
  id: number,
  name: string
}

// Define the props that getInitialProps will inject into the component
interface IListClassProps {
  items: IDataObject[]
}

class List extends React.Component<IListClassProps> {
  static async getInitialProps({ pathname }: NextContext) {
    const dataArray: IDataObject[] =
      [{ id: 101, name: 'larry' }, { id: 102, name: 'sam' }, { id: 103, name: 'jill' }, { id: 104, name: pathname }]

    return { items: dataArray }
  }

  render() {
    return (
      <ul>
        {this.props.items.map((item) => (
          <li key={item.id}>
            {item.id} -- {item.name}
          </li>
        ))}
      </ul>
    )
  }
}

export default List

如果您查看 tests in DefinitelyTyped,您可以深入了解如何为 Next 使用其他类型的变体。

编辑 2021/04/23:我下面的回答也已过时

请参阅 the Next.js docs 了解当前关于使用新类型和命名导出输入数据获取方法的建议。

tl;博士:

import { GetStaticProps, GetStaticPaths, GetServerSideProps } from 'next'

export const getStaticProps: GetStaticProps = async (context) => {
  // ...
}

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
}


答案已过时(但功能正常):

以上答案已过时,因为 Next.js 现在正式支持 TypeScript(公告 here

此版本的一部分是更好的 TypeScript 类型,其中大部分 Next.js 是用 TypeScript 本身编写的。这意味着 @types/next 包将被弃用,取而代之的是官方 Next.js 类型。

相反,您应该导入 NextPage type 并将其分配给您的组件。您还可以使用 NextPageContext 类型键入 getInitialProps

import { NextPage, NextPageContext } from 'next';

const MyComponent: NextPage<MyPropsInterface> = props => (
  // ...
)

interface Context extends NextPageContext {
  // any modifications to the default context, e.g. query types
}

MyComponent.getInitialProps = async (ctx: Context) => {
  // ...
  return props
}

遵循文档

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>
  }
}

简单的解决方案是声明类型:

declare global {
   namespace React {
      interface FunctionComponent<P = {}> {
      getInitialProps(): void;
}}}