Использование getInitialProps в Next.js с помощью TypeScript

Из документации, объявления Next.js 5.0 и различных статей в Интернете кажется, что Next.js хорошо поддерживает TypeScript, и многие используют его.

Но эти потоки предполагают, что getInitialProps, жизненно важный для приложений Next.js, не работает:

Как я могу это исправить? Как в классе, так и в функциональных компонентах, когда я делаю ComponentName.getInitialProps = async function() {...} я получаю следующую ошибку:

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

Ответ 1

ОБНОВЛЕНИЕ: этот ответ устарел с момента выпуска 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) => {...}

Ответ 2

Приведенные выше ответы устарели, поскольку Next.js теперь официально поддерживает TypeScript (объявление здесь)

Часть этого выпуска - лучшие типы TypeScript, большая часть Next.js написана на самом TypeScript. Это означает, что пакет @types/next будет заменен официальными наборами Next.js.

Вместо этого вы должны импортировать тип NextPage и назначить его своему компоненту. Вы также можете ввести getInitialProps, используя тип NextPageContext.

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
}

Ответ 3

Типы для Next.js поддерживаются в проекте DefinitiveTyped, который имеет новую версию 7.0.6.

Чтобы использовать новые типы, убедитесь, что вы импортируете их в свой проект:

npm install --save-dev @types/[email protected]

Вот как вы вводите 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 для класса:

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

Если вы ознакомитесь с тестами в DefiniteTyped, вы сможете получить много полезных советов о том, как использовать другие варианты типов для Next.

Ответ 4

Это не решение, но возможное обходное решение - использовать TypeScript только для серверных и общих скриптов (расширение .ts).

Для компонентов React используйте расширение .jsx и откажитесь от TypeScript. prop-types и prop-types-exact можно использовать вместо них - они дают проверку типа для реквизита (установите пакеты с помощью npm).