ちょっとしたアプリケーションがほしいなと思っていて、勉強がてらTypeScriptとかNode.jsを使ってみよう→まずはチュートリアルで感覚を掴もう、と信頼できそうなサイトを見つけたが、これがまた曲者でなかなか前に進まず苦労している。
動作環境
閲覧ページなどの情報はすべて執筆時点のもの。
- Windows11
- Node.js v20.12.2
- Visual Studio Code
Node.jsの動作の仕組み
序盤は問題なく動作する。しかし「最上位の async/await」でpackage.jsonを追加すると、その後のプログラムが動かなくなる。
ReferenceError: require is not defined in ES module scope, you can use import instead
どうやら "type":"module"
を指定すると require
が使えないらしい。メッセージ通り import
を使う方法に切り替えた。
// サンプルコードのrequireを止める // const fs = require('fs').promises; // importに置き換える import * as fs from 'node:fs/promises';
"type":"module"
を指定し続ける限り同じ問題が発生するので、その後のサンプルコードも同様の処置が必要。あるいはディレクトリを分けるとか、package.jsonを削除するといった対応でも動作する。多分 "type":"module"
が実際の開発のスタンダードだろうから、import
を使った方がいいんだろうなという気はする。
Node.js を試す
REPLやHello, worldは問題なく動く。しかしその次の「非同期関数を追加する」が恐らく間違っている。
前述の通りトップレベルで await
を使うためにはpackage.jsonで "type":"module"
の指定が必要で、それをやると require
が使えなくなるので import
に置き換えなければならない。しかしこのサンプルコードは置き換えてもきちんと動作せず、実行するとステータスコード(200)が表示されるべきところが undefined
になる。
公式リファレンスにしたがってコールバック関数を渡すと問題なく動作する。
HTTPS | Node.js v20.12.2 Documentation
※ ページ内リンクがうまく働いてくれない……。何回か読み込み直すと良い感じに表示される時もある
コールバック関数なら動くがトップレベルの await
では動かない。ついでに単純なasync-await構文でも動かない。
いろいろ試した結果、await
を用いた場合の戻り値は ClientRequest
型で、この中に statusCode
プロパティは存在しないことが判明した。 Request
なんだからそりゃそうだよな。
もう一度公式リファレンスに戻って、今度は ClientRequest
の説明を読む。
HTTP | Node.js v20.12.2 Documentation
To get the response, add a listener for 'response' to the request object. 'response' will be emitted from the request object when the response headers have been received. The 'response' event is executed with one argument which is an instance of http.IncomingMessage.
レスポンスを受け取るには response
イベントに対してリスナーを設定する必要がある……らしい。説明全体を読んでも Promise
がどうこうといった話は出てこない。
まさかそんなことはないだろうと思いたかったが、どうやらそもそもの話、このサンプルコード全体が間違っていると結論づけるしかなかった。まさか天下のマイクロソフト様が作られたチュートリアルでそんなことがあろうとは。今でも半信半疑なので、私の勘違いを指摘してくれるコメントを待っています。
ということで「非同期関数を実装する」のサンプルコードは、非同期関数を使わずに実装しないと動かない。かもしれない。
import * as https from 'node:https'; console.log(`start`); https.get('https://nodejs.org/dist/index.json', res => { console.log(res.statusCode); }); console.log(`end`);
await
を使わないので require
のままでもいいんだけど、基本的に "type":"module"
で進めることに決めたのでそこだけは直していない。
チュートリアルだから、サンプルコードだから、と流さずひとつひとつの問題と向き合うことで知識は深まったが、それにしてもこんな誤りが……あるのか……?と信じがたい気持ちは拭えない。上記2つは両方ともフィードバックを送信したので、運が良ければいつか直っている、かもしれない。