質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

4467閲覧

Next.js + TypeScriptでシンタックスエラーが出てしまう。

f6ae

総合スコア92

Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2020/09/03 15:01

編集2020/09/04 15:46

質問

Next.js + TypeScriptの勉強のため以下のリポジトリの改造を試みていますが、
序盤で詰まってしまいました。
https://github.com/Daniel-Kao/nextjs-demo

発生している問題・エラーメッセージ

web/pages/dashboards.ts

import React from "react" import { AuthProps, privateRoute } from "../components/private_route"; import Cookie from "js-cookie"; import Router from "next/router"; import { COOKIES } from "../services/login_service"; import { Links } from "../components/links"; import { get } from "../services/rest_service"; import { AuthToken } from "../services/auth_token"; type Props = AuthProps & { message: string } function Page(props: Props) { const logout = async () => { Cookie.remove(COOKIES.authToken); alert("logout"); await Router.push("/login"); }; console.log(props.auth); return <> <Links /> <p>{props.message}</p> {/*<div>{props.auth.authorizationString}</div>*/} <p>isAuthenticated:{props.auth.isAuthenticated ? "YES" : "NO"}</p> <button onClick={logout}>Logout</button> </> } Page.getInitialProps = async ({ auth }: AuthProps): Promise<Props> => { const res: any = await get("/api/restricted", { headers: { Authorization: auth.authorizationString } }); let message = "Something unexpected happened!"; if (res.error) { message = res.error; } else if (res.data && res.data.message) { message = res.data.message } return { message, auth, }; }; export default privateRoute(Page);


・functionをconstに変更
・型安全のため「NextPage」を使用(ここは知識が曖昧)
するため下記のように書き換えました。

import React from "react" import { AuthProps, privateRoute } from "../components/private_route"; import Cookie from "js-cookie"; import Router from "next/router"; import { COOKIES } from "../services/login_service"; import { Links } from "../components/links"; import { get } from "../services/rest_service"; import { AuthToken } from "../services/auth_token"; import { NextPage } from "next"; type Props = AuthProps & { message: string } const Page : NextPage<Props> = (props : Props) => { const logout = async () => { Cookie.remove(COOKIES.authToken); alert("logout"); await Router.push("/login"); }; console.log(props.auth); return <> <Links /> <p>{props.message}</p> {/*<div>{props.auth.authorizationString}</div>*/} <p>isAuthenticated:{props.auth.isAuthenticated ? "YES" : "NO"}</p> <button onClick={logout}>Logout</button> </> } Page.getInitialProps = async ({ auth }: AuthProps): Promise<Props> => { const res: any = await get("/api/restricted", { headers: { Authorization: auth.authorizationString } }); let message = "Something unexpected happened!"; if (res.error) { message = res.error; } else if (res.data && res.data.message) { message = res.data.message } return { message, auth, }; }; export default privateRoute(Page);

しかし、書き換えたところ、
「const Page」の部分で

型 '{ (props: Props): JSX.Element; getInitialProps({ auth }: AuthProps): Promise<Props>; }' を型 'NextComponentType<NextPageContext, {}, {}>' に割り当てることはできません。 型 '{ (props: Props): JSX.Element; getInitialProps({ auth }: AuthProps): Promise<Props>; }' を型 'FunctionComponent<{}> & { getInitialProps?(context: NextPageContext): {} | Promise<{}>; }' に割り当てることはできません。 型 '{ (props: Props): JSX.Element; getInitialProps({ auth }: AuthProps): Promise<Props>; }' を型 'FunctionComponent<{}>' に割り当てることはできません。 パラメーター 'props' および 'props' は型に互換性がありません。 型 '{ children?: ReactNode; }' を型 'Props' に割り当てることはできません。 プロパティ 'auth' は型 '{ children?: ReactNode; }' にありませんが、型 'AuthProps' では必須です。ts(2322)

「Page.getInitialProps」の部分で、

型 '({ auth }: AuthProps) => Promise<Props>' を型 '(context: NextPageContext) => {} | Promise<{}>' に割り当てることはできません。 パラメーター '__0' および 'context' は型に互換性がありません。 プロパティ 'auth' は型 'NextPageContext' にありませんが、型 'AuthProps' では必須です。

とのシンタックスエラーが出てきてしまいました。

質問

知りたい点は以下です。
(1)NextPageは型安全を保証するための機能という認識で間違いないでしょうか?
(2)上記のコードは何故シンタックスエラーになるのでしょうか。どのように修正すれば良いでしょうか。

初心者の為見当外れな事をしているかもしれません。至らない点があれば申し訳ございません。

試したこと

検索しながら、コードを変更したり削ったり試行錯誤して数時間格闘しましたが、
分かりませんでした。。。

補足情報(FW/ツールのバージョンなど)

Next.js 9.5
TypeScript 3.5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

自分もNext.js初心者なので参考程度に読んで下さい。

NextPageは型安全を保証するための機能という認識で間違いないでしょうか?

それもありますし、NextPageの場合はページコンポーネントだということを明示的にして、部品コンポーネントと区別するといった意味もあると思います。

ちなみにNextPageに相当するものを自分で作ると以下のような感じになると思います。
単にgetInitialPropsの型を定義しているだけなので、型安全という点では使うメリットは少ないのかもしれません。

typescript

1interface MyPage<P = {}> extends React.SFC<P> { 2 getInitialProps?: (context: NextPageContext) => P | Promise<P> 3} 4 5const Page: MyPage<Props> = (props: Props) => { 6}

何故シンタックスエラーになるのでしょうか。どのように修正すれば良いでしょうか。

エラーメッセージを見ると、

型 '({ auth }: AuthProps) => Promise<Props>' を型 '(context: NextPageContext) => {} | Promise<{}>' に割り当てることはできません。

とあるので、getInitialPropsの引数の型が間違っているということだと思います。
authAuthPropsが何なのかよく分からないので、全体のコードは示せませんが、少なくともgetInitialPropsは以下のように引数でNextPageContext型、またはそれに準拠した型を受け取る関数になるはずです。

typescript

1Page.getInitialProps = async (context: NextPageContext): Promise<Props> => { 2}

投稿2020/09/05 00:59

編集2020/09/05 04:20
nskhei

総合スコア704

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

f6ae

2020/09/05 12:50 編集

回答どうも有り難うございます。 いただいたご回答によりNextPageの意味が若干理解出来まして、 以下のページのようにNextPageを継承しauthを含めた`NextPageWithAuth`を作成すれば良いのではないかと思い、試してみました。 https://qiita.com/github0013@github/items/6f64f294f0a1777e9c84#page Next.jsのバージョンの違いのためかNextPageがinterfaceではなくtypeであるため継承が出来ず、頂いた回答を手本に以下のようにinterfaceを作成しました。 ``` import React from "react" import { AuthProps, privateRoute } from "../components/private_route"; import Cookie from "js-cookie"; import Router from "next/router"; import { COOKIES } from "../services/login_service"; import { Links } from "../components/links"; import { get } from "../services/rest_service"; import { AuthToken } from "../services/auth_token"; import { NextPageContext } from "next"; type Props = AuthProps & { message: string } interface NextPageWithAuth<P = {}> extends React.SFC<P> { getInitialProps?: (auth: AuthProps, context: NextPageContext) => P | Promise<P> } const Page : NextPageWithAuth<Props> = (props : Props) => { const logout = async () => { Cookie.remove(COOKIES.authToken); alert("logout"); await Router.push("/login"); }; console.log(props.auth); return <> <Links /> <p>{props.message}</p> {/*<div>{props.auth.authorizationString}</div>*/} <p>isAuthenticated:{props.auth.isAuthenticated ? "YES" : "NO"}</p> <button onClick={logout}>Logout</button> </> } Page.getInitialProps = async ({ auth } : AuthProps): Promise<Props> => { const res: any = await get("/api/restricted", { headers: { Authorization: auth.authorizationString } }); let message = "Something unexpected happened!"; if (res.error) { message = res.error; } else if (res.data && res.data.message) { message = res.data.message } return { message, auth, }; }; export default privateRoute(Page); ``` これで一応動くは動くようにはなったのですが、 `getInitialProps?: (auth: AuthProps, context: NextPageContext) => P | Promise<P>` を `getInitialProps?: (auth: AuthProps => P | Promise<P>` に変更しても動いてしまい、果たしてこれは意味のある行為なのか...?というところで悩みまた迷宮にハマってしまいました。。。 authに関してはログイン情報が入っており、ログインされている時にはDashBoardを表示、ログインされていない時にはLoginページに繊維するという 作りになっています。 今、頭にある疑問点としては以下です。 (1)やはり`const Page : NextPage<Props> = (props : Props) => {`とした時に出るシンタックスエラーの解消方法が分からない。 (2)`async ({ auth } : AuthProps)`でauthにログイン情報が入るようだが、なぜこの記述で入るのか?(asyncで調べてもasync functionの例しか出てこずいまいち動きが分かりません。)
nskhei

2020/09/05 22:41

なぜログイン情報が入るかは分からないですが、contextにauthというプロパティが付与されるのであれば、 以下のようにNextPageContextを拡張した型を作るというのはどうでしょうか? ``` interface CustomPageContext extends NextPageContext { auth: AuthProps; } ``` 以下のように使います。 ``` Page.getInitialProps = async (context: CustomPageContext): Promise<Props> => { console.log(context.auth); // こんな感じでログイン情報にアクセスできる(?) ``` これだとNextPageで定義されているgetInitialPropsの型にマッチするので、型エラーはなくなるのではないかと思います。
f6ae

2020/09/06 10:37 編集

返信有り難うございます。頂いた情報を元に以下のように書き換えたところ、 `CustomPageContext<Props>`の部分で `型 'CustomPageContext' はジェネリックではありません。` `context.auth.authorizationString`の部分で `プロパティ 'authorizationString' は型 'AuthProps' に存在しません。` `context.auth`の部分で `型 '{ message: string; context: CustomPageContext; "": any; }' を型 'Props' に割り当てることはできません。オブジェクト リテラルは既知のプロパティのみ指定できます。'context' は型 'Props' に存在しません。` とのシンタックスエラーが出てしまいました。authはasync部に直接書かないと機能しないのでしょうかね。。。 ``` import React from "react" import { AuthProps, privateRoute } from "../components/private_route"; import Cookie from "js-cookie"; import Router from "next/router"; import { COOKIES } from "../services/login_service"; import { Links } from "../components/links"; import { get } from "../services/rest_service"; import { AuthToken } from "../services/auth_token"; import { NextPageContext } from "next"; type Props = AuthProps & { message: string } interface CustomPageContext extends NextPageContext { auth: AuthProps; } const Page : CustomPageContext<Props> = (props : Props) => { const logout = async () => { Cookie.remove(COOKIES.authToken); alert("logout"); await Router.push("/login"); }; console.log(props.auth); return <> <Links /> <p>{props.message}</p> {/*<div>{props.auth.authorizationString}</div>*/} <p>isAuthenticated:{props.auth.isAuthenticated ? "YES" : "NO"}</p> <button onClick={logout}>Logout</button> </> } Page.getInitialProps = async (context: CustomPageContext): Promise<Props> => { const res: any = await get("/api/restricted", { headers: { Authorization: context.auth.authorizationString } }); let message = "Something unexpected happened!"; if (res.error) { message = res.error; } else if (res.data && res.data.message) { message = res.data.message } return { message, context.auth, }; }; export default privateRoute(Page); ```
nskhei

2020/09/06 10:55

こんな感じでどうでしょうか? ・CustomPageContextはgetInitialPropsの引数の型です。 ・ページコンポーネントには普通にNextPageを使います。 ・asyncは関数内でawaitを使えるようにするための構文なので、関係ありません。 ``` import React from 'react'; import { AuthProps, privateRoute } from '../components/private_route'; import Cookie from 'js-cookie'; import Router from 'next/router'; import { COOKIES } from '../services/login_service'; import { Links } from '../components/links'; import { get } from '../services/rest_service'; import { AuthToken } from '../services/auth_token'; import { NextPageContext, NextPage } from 'next'; type Props = AuthProps & { message: string; }; interface CustomPageContext extends NextPageContext { auth: AuthProps; } const Page: NextPage<Props> = (props: Props) => { const logout = async () => { Cookie.remove(COOKIES.authToken); alert('logout'); await Router.push('/login'); }; console.log(props.auth); return ( <> <Links /> <p>{props.message}</p> <div>{props.auth.authorizationString}</div> {props.auth.isAuthenticated ? 'YES' : 'NO'} <button onClick={logout}>Logout</button> </> ); }; Page.getInitialProps = async (context: CustomPageContext): Promise<Props> => { const res: any = await get('/api/restricted', { headers: { Authorization: context.auth.auth.authorizationString, }, }); let message = 'Something unexpected happened!'; if (res.error) { message = res.error; } else if (res.data && res.data.message) { message = res.data.message; } return { message, ...context.auth }; }; export default privateRoute(Page); ```
f6ae

2020/09/06 11:31

出来ました!! ページコンポーネントは普通にNextPageを使用するというのは目からうろこです。 この場合getInitialPropsの引数の型は`NextPageContext`としなければならないと考えていたのですが、`CustomPageContext`でも大丈夫なのは、継承しているから大丈夫ということなんでしょうか? `context.auth.auth.authorizationString,`の部分も、なるほど、と思いました。 また同時に自分でも別の方法で解決しました。 async部からauthを取り除き、内部の処理で`auth = new AuthToken;`として実体化してあげれば良いだけでした。 つまり`async ({ auth }: AuthProps)`にてAuthPropsを実体化してくれていたのだと分かりました。 分かってしまえば拍子抜けでしたが、nskhei様に頂いた回答で理解が深まり、解決することが出来ました。 やっと次に進む事ができそうです。本当に有り難うございます。 ``` import React from "react" import { AuthProps, privateRoute } from "../components/private_route"; import Cookie from "js-cookie"; import Router from "next/router"; import { COOKIES } from "../services/login_service"; import { Links } from "../components/links"; import { get } from "../services/rest_service"; import { AuthToken } from "../services/auth_token"; import { NextPage } from "next"; type Props = AuthProps & { message: string } const Page : NextPage<Props> = (props : Props) => { const logout = async () => { Cookie.remove(COOKIES.authToken); alert("logout"); await Router.push("/login"); }; console.log(props.auth); return <> <Links /> <p>{props.message}</p> {/*<div>{props.auth.authorizationString}</div>*/} <p>isAuthenticated:{props.auth.isAuthenticated ? "YES" : "NO"}</p> <button onClick={logout}>Logout</button> </> } Page.getInitialProps = async (): Promise<Props> => { const auth = new AuthToken; const res: any = await get("/api/restricted", { headers: { Authorization: auth.authorizationString } }); let message = "Something unexpected happened!"; if (res.error) { message = res.error; } else if (res.data && res.data.message) { message = res.data.message } return { message, auth, }; }; export default privateRoute(Page); ```
nskhei

2020/09/06 11:43

解決できてよかったです! こちらこそとても勉強になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問