◆ 結果の Dir 型の read メソッドで Dirent を取得できる
  ◆ これまで readdir で取得できたもの

Node.js は v10 以降 fs 周りの変更が多めな気がします

fs.promises で Promise 版メソッドが増えたり
各種非同期メソッドの callback が必須になったり (たぶん callback なしが通ると Promise が返ってくると思われそうだから)
mkdir に recursive オプションが増えたり
rmdir に recursive オプションが増えたり
open の flags が省略可能でデフォルトが r になったり
readdir で withFileTypes を指定できるようになったり

withFileTypes は気づかず普通に使ってましたけどけっこう最近追加されたものだったんですね

opendir

そして今回気づいた機能は opendir で新規追加のメソッドです
12.12 で追加されていました

readdir の withFileTypes と関連するものです
readdir で withFileTypes を指定するとファイル名の代わりに Dirent という型で情報を得ることができます
dirent は directory entry の略みたいです

> fs.readdirSync(".", { withFileTypes: true })
[
Dirent { name: '.bash_history', [Symbol(type)]: 1 },
Dirent { name: '.bash_logout', [Symbol(type)]: 1 },
Dirent { name: '.bashrc', [Symbol(type)]: 1 },
Dirent { name: '.cache', [Symbol(type)]: 2 },
...
]

opendir では Dir という型を得られてその read メソッドで得られるのが Dirent 型のデータです
一気に全部の Dirent を取得せず順番にひとつずつ Dirent を取得したい場合に使えます

> const dir = fs.opendirSync(".")
> dir
Dir {
[Symbol(kDirHandle)]: DirHandle {},
[Symbol(kDirPath)]: '.',
[Symbol(kDirClosed)]: false,
[Symbol(kDirOptions)]: { encoding: 'utf8' },
[Symbol(kDirReadPromisified)]: [Function: bound read],
[Symbol(kDirClosePromisified)]: [Function: bound close]
}
> dir.readSync()
Dirent { name: '.bash_history', [Symbol(type)]: 1 }
> dir.readSync()
Dirent { name: '.bash_logout', [Symbol(type)]: 1 }
> dir.readSync()
Dirent { name: '.bashrc', [Symbol(type)]: 1 }

試すのに楽なので Sync を使ってますが sync なしもあります
read に引数でコールバック関数を渡すと 引数に結果を入れて実行されるのはいつもどおりですが 何も渡さなければ Promise 型が得られます
ライブラリにはよくある形ですが fs のメソッドは callback の省略は禁止で Promise 型で返してほしいなら fs.promises 版を使う必要があるのでちょっと他と違います

全部読み取り終わるとあとは null が返ってきます

また Symbol.asyncIterator が実装されているので for await of が使えます

!async function() {
const dir = await fs.promises.opendir(".")
for await (const dirent of dir) {
console.log(dirent)
}
}()
Dirent { name: '.bash_history', [Symbol(type)]: 1 }
Dirent { name: '.bash_logout', [Symbol(type)]: 1 }
Dirent { name: '.bashrc', [Symbol(type)]: 1 }
Dirent { name: '.cache', [Symbol(type)]: 2 }
...

別にこうしてもいいんですけどね

!async function() {
const dir = await fs.promises.opendir(".")
let dirent
while (dirent = await dir.read()) {
console.log(dirent)
}
}()

あとは 一応 open なわけですから close メソッドがあります
見た感じ read で全部読み取り終わっても close されないようなので自分で close 必要です
close すると続きがあっても取得できなくなります

close 必要になってくると C# の using とか python の with みたいなのが欲しくなりますね

const using = (closable, fn) => {
try {
const maybe_promise = fn(closable)
if (maybe_promise && typeof maybe_promise.finally === "function") {
return maybe_promise.finally(() => closable.close())
} else {
const maybe_promise2 = closable.close()
if (maybe_promise2 && typeof maybe_promise2.then === "function") {
return maybe_promise2.then(() => maybe_promise)
} else {
return maybe_promise
}
}
} catch (err) {
closable.close()
throw err
}
}

using(await fs.opendir("."), dir => {
// something
})