在 Next.js 中使用 getInitialProps 和 TypeScript
Using getInitialProps in Next.js with TypeScript
从文档、Next.js 5.0 公告和互联网上的各种文章来看,Next.js 似乎很好地支持 TypeScript,而且很多人都在使用它。
但是这些线程表明 getInitialProps
对 Next.js 应用至关重要,但它不起作用:
- https://github.com/zeit/next.js/issues/3396
- https://github.com/zeit/next.js/issues/1651
- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/23356
我该如何解决?在 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;
}}}
从文档、Next.js 5.0 公告和互联网上的各种文章来看,Next.js 似乎很好地支持 TypeScript,而且很多人都在使用它。
但是这些线程表明 getInitialProps
对 Next.js 应用至关重要,但它不起作用:
- https://github.com/zeit/next.js/issues/3396
- https://github.com/zeit/next.js/issues/1651
- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/23356
我该如何解决?在 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;
}}}