◆ then の中でメソッド呼び出すのを直接できるように

よくある Promise のチェーンで毎回 then が入るのが面倒です
こういうのです

fetch(url).then(res => res.json())

理想は

fetch(url).json()

という感じで then を省略できるものです
構文上 メソッド名が事前にわからない場合は 事前にプロパティを用意しておけないので Proxy 必須です
Proxy は devtools と相性悪かったり変に複雑になるのでテスト用みたいなところ以外ではあまり使いたくないのですが とりあえず作ってみました
これがライブラリ部分です

const p = prm =>
new Proxy(Object.assign(() => {}, { pendings: [] }), {
get(ctx, name, pxy) {
ctx.pendings.push(name)
return pxy
},

apply(ctx, pxy, args) {
const pendings = ctx.pendings
ctx.pendings = []
const last = pendings.pop()
if (last == null) return pxy

if (pendings.length) {
prm = prm.then(value => {
for (const name of pendings) {
value = value[name]
}
return value
})
}

if (last === "then") {
return prm.then(...args)
} else {
prm = prm.then(value => value[last](...args))
return pxy
}
},
})

最初の Promise を返す関数を p でラップしてからプロパティアクセスします
then を明示的に呼び出すと引数で渡した関数の引数に そこまでチェーンした結果を入れて呼び出します

例 1

p(new Promise(x => setTimeout(x, 1000, { a: 1 }))).a.then(console.log)
// 1

普通は プロパティ a は then なしではアクセスできないはずですが普通にプロパティで取得しています

例 2

fetch の場合はこうなります
fetch する URL は JSON で中身は 「{"x":{"y":{"z":10}}}」 です

await p(fetch(url)).json().x.y.z
// 10

then の明示的呼び出しは await すれば内部で自動的にやってくれるので 見た目的には普通にプロパティアクセスしてるだけになります

例 3

こういう関数があったとします

const x = () => {
let t = ""
const f = s => new Promise(r => {
t += s
r({
a() {return f("a")},
b() {return f("b")},
c() {return f("c")},
value: t,
})
})
return f("")
}

こういう then のメソッドチェーンをしたいとします

x().then(v => v.a()).then(v => v.b()).then(v => v.c()).then(v => console.log(v.value))
// abc

それがこれで済みます

p(x()).a().b().c().then(v => console.log(v.value))
// abc

await と比べると

await すれば then はいらないので そこまで不便ではないのですが await を一行で書くと見づらくなります
一行にしなければそんなことはないのですが 1 つの式に収めたいとか 中間の値でスコープ中に不要な変数を増やしたくないなどが理由で一行で書きたいときはそこそこあります

1 つの式にする場合 await の括弧が必要なので

const data = await (await fetch(url)).json()

のようになってチェーンが長くなるほど見づらくなっていきます

そう考えると このツールはけっこうアリかなって思うのですけど 最初に書いたとおり Proxy なのでちゃんとしたところで使うのはちょっと抵抗あります
console でちょっと試すときに楽にかけるくらいな扱いですかねー