どうもー、おはようございます。マイクです。
朝7時をちょっと回ったところ、2025年12月25日、木曜日の朝ですね。
ここからの時間は、Zennで話題になっているトレンド記事をゆるっと楽しく紹介していく「zenncast」、最後までお付き合いください。
さて今日は、全部で5本の記事をご紹介していきます。TypeScriptの型テクから、新しいUIフレームワーク、AI時代の仕様駆動開発、爆速Lint環境、そしてGoの細かすぎるけど気になる最適化の話まで、エンジニア心をくすぐるラインナップになってます。
まず1本目。「今年お世話になったTypeScriptの型集」という記事です。
これはね、普段TypeScriptを書いている方には刺さりまくる内容だと思います。筆者が2025年に実務でガンガン使い倒した「自作ユーティリティ型」をまとめてくれていて、単なるTipsじゃなくて「どうprops設計すると安全になるか」「OpenAPIから生成した型をどう網羅チェックするか」みたいな、現場目線の話がぎっしりです。例えば、オブジェクトの値のユニオンを取り出す`ValueOf`を、さらにネスト対応にした`DeepValueOf`で「この巨大なレスポンスのどこまでちゃんと扱えてる?」を型で確認したり、フォームや設定画面のオプションで「このキーのうち1つだけ必須にしたい」っていう、JSだけだとついテスト頼みになりがちな制約を`OneOf`や`OrOf`でガチっと型に落とし込んでます。全部指定するか、まったく持たないかだけ許す`AllOrNone`とか、「値はオプショナルだけど、とりあえずキーは絶対に定義させたい」みたいな`RequiredButPossiblyUndefined`も、Reactのprops設計とかで「あ〜これほしかった!」ってなるやつですね。さらにTypeScript 5.4で入った`NoInfer`をうまく使って、ジェネリクスの推論をあえて止めるテクも紹介されていて、React Hook Formのラッパーコンポーネントで「型引数をちゃんと指定しないと動かない」構造にするとか、かなり攻めたテクニックが出てきます。全体を通して、「型でどこまで守備範囲を広げられるか」を考えるきっかけになる記事でした。タイプレベルの設計に興味ある方は要チェックです。。.。。
続いて2本目。「Luna UI - JS/Moonbit のための宣言的UI. 軽量、高速、そして WebComponents First」という記事です。
これは、Moonbitっていう言語や、WebComponents周辺が気になっている人にはめちゃくちゃ面白い内容でした。筆者が、既存のReactとかQwikに感じていた「重い・設定やコンパイルがしんどい・SSRやWebComponentsまわりがスッキリしない」といった不満を全部まとめて、自分で新しいUIライブラリ「Luna UI」を作ってしまった、というストーリーになっています。Lunaはシグナルベースの宣言的UIで、APIの雰囲気はpreactっぽいのに、コアが6〜7kbくらいのかなり小さなサイズ。コンパイル時のゴリゴリした最適化に頼らなくても、それなりに高速に動くように設計されているのがポイントです。特に面白いのが、「WebComponents前提」でSSRとHydrationを考えているところで、Declared Shadow DOMに対応してたり、Cloudflare Workers上で動くNext.js的なフレームワーク「Sol」、ドキュメント用SSG「Astra」まで、全部Lunaの上に積み上げているという、かなり野心的な構成になっています。パフォーマンス検証としても、100×100のDOMを毎フレーム更新するシューティングゲームとか、定番のTodoMVCを動かして、高FPSをキープしつつ軽量な実装になっていることを示していて、「軽くてシンプル、それでいて実用的」を目指した設計思想が伝わってきます。MoonbitがJSだけじゃなく、ネイティブやWasm-GCにも吐けるマルチターゲット言語なので、将来的に「高速SSR+Wasm」の構成も視野に入れているのがめちゃ熱いですね。今後はWebComponentsまわりのアクセサリやヘッドレスUI、ネイティブSSRも整えていきたいということで、フィードバックお待ちしてます、という呼びかけもされています。新しいUIの波、気になる方はウォッチしておくと良さそうです。。.。。
3本目。「実務で使い倒したので cc-sdd の仕様駆動開発プロセスを丁寧に解説してみた」という記事です。
これはAIコーディング時代の「ちゃんとした開発プロセスどうする問題」に、かなり真っ向から向き合った内容でした。cc-sddは、Claude CodeとかCursor向けの「仕様駆動開発ツール」で、いきなり実装をAIに書かせるんじゃなくて、その前に「要件・設計・実装計画」をしっかり承認するゲートを置く、というコンセプトになっています。まずはSteeringドキュメントっていう、プロジェクト全体の記憶を残すファイルを用意して、「技術スタック」「コーディング規約」「設計の前提」なんかをここに集約。これを全セッションで読み込ませることで、AIとの会話が毎回フレッシュスタートにならないようにする、っていう発想ですね。機能要件はEARS形式っていう定型フォーマットに自動変換して、曖昧な表現を減らしていきます。その上で、design.mdに設計を書き出してレビューし、それがOKになったらtasks.mdに具体的なタスクへ落とし込む。タスクごとにどの要件に紐づいているかも管理するので、「この要件やってなくない?」っていう漏れを防ぎやすくなっています。実装フェーズでは、そのSteering・要件・設計・タスク、全部ひっくるめたコンテキストをAIに渡すことで、方向性がブレにくくなります。さらに検証用コマンドで、要件やタスクの網羅性を自動チェックできるのもポイント。筆者は、「なんでもかんでもSDDやろう」じゃなくて、軽い変更やプロトタイピングは普通のAI駆動開発でOK、大きな機能追加やセキュリティ・性能が絡むところではSDDを強くおすすめ、という線引きをしていて、このバランス感覚も参考になります。特にチーム開発で、ジュニアメンバーの要件の抜け漏れをカバーしたり、設計レビューの共通フレームを作る上で、かなり効いてくるツールだなと感じました。。.。。
4本目。「oxlint, oxfmt, tsgoを導入して開発環境が爆速になった」という記事です。
これは、TypeScriptの巨大コードベースを抱えているチームには朗報な内容ですね。フロントとバックを合わせてTSファイルが1万を超える大規模プロジェクトで、従来のESLint / Biome / Prettier / tscを、Rust製のoxcツールチェーン(oxlint, oxfmt)と、TypeScript公式のtsgoに置き換えた結果、CI時間がガッツリ短縮できたという事例になっています。バックエンド側だと、Lintが52秒から4秒まで、約92%削減。型チェックも105秒から36秒まで減っていて、ESLint関連の複数パッケージをoxlintひとつに集約できたことで、依存関係もスッキリしたそうです。一方で、フロントエンドでは、oxlintに`--type-aware`をつけて型情報をフル活用するようにした結果、Lint時間は2秒から14秒に増えたんですが、その代わりにtsgoとの連携で、Promiseの誤用みたいな「テストでは見逃しがちな実行時バグ」を事前に検出できるようになって、あえて品質側に振ったトレードオフを選んだとのこと。設定例として、npm scriptsや.oxlintrc、.oxfmtrcでimport循環検出や自動ソートも組み込んでいて、「移行のとき何をいじればいいか」がイメージしやすくなっています。tsgoはtscよりも厳しめなので、初回導入時は大量の違反が出て、「全部一気に直すのは無理!」ということで、warnにして段階的に潰したり、`@ts-expect-error`を活用して制御していった話も正直ベースで書かれていました。Type-Aware Lintingでメモリがガンっと増える問題についても、CIのメモリを増やして対応した、といった現実的な工夫が載っていて、同じ課題を抱えているチームにとって、かなり参考になりそうです。oxcもtsgoもまだ進化途中ではあるんですが、「今の時点でもこれだけ速くなるし、今後もっと良くなりそうだから、試す価値あるよね」という結論で締められています。。.。。
そして最後、5本目。「Go 1.24でmap[T]struct{}のメモリ節約効果がなくなった」という記事です。
Go書いている方なら一度は聞いたことがある、「存在判定用のmapにはboolじゃなくてstruct{}を使うとメモリ得だよ」というお約束。それが、Go 1.24からはほぼ意味なくなりましたよ、という検証記事です。筆者がmacOS / arm64環境で、100万要素の`map[int]bool`と`map[int]struct{}`を、Go 1.20, 1.23, 1.24, 1.25で計測したところ、1.20と1.23では約9%くらいstruct{}の方が節約できていたのに、1.24以降はどちらも37.83MBで完全に同じ、という結果になったそうです。その背景にあるのが、Go 1.24で導入されたSwissTable方式の新しいmap実装。従来は、「キーの配列」と「値の配列」が分かれていて、値が`struct{}`ならその値領域を事実上ゼロにできたんですが、新しい実装では、スロットの中に`{key, value}`が交互に置かれるレイアウトになりました。このとき、ゼロサイズのstruct{}でもアライメントやゼロサイズフィールドの扱いの都合でパディングがついてしまって、結果として`key + struct{}`も、`key + bool`も16バイト消費する、という状態になってしまったわけですね。この挙動はすでにissue #71368として認識されていて、今後のGo 1.26以降で、フィールド順序を工夫したり、`KKKKVVVV`みたいな別レイアウトにしたりして、再び`map[T]struct{}`のメモリ効率を改善できないか検討中とのこと。なので、現時点では「struct{}にしてもメモリ的には変わらないけど、将来的にまた有利になる可能性はある」という感じです。昔からのベストプラクティスが、ランタイム実装の変化でどう変わっていくか、という良い事例になっていました。
というわけで、今日は
・TypeScriptの強力な自作ユーティリティ型まとめと、`NoInfer`を使った型安全テク
・Moonbit発の軽量宣言的UI「Luna UI」と、WebComponents Firstな設計思想
・AIコーディング時代に手戻りを減らす「cc-sdd」の仕様駆動プロセス
・oxlint / oxfmt / tsgoで大規模TSプロジェクトのCIを爆速&高品質にした話
・Go 1.24以降で`map[T]struct{}`のメモリ節約が消えた理由と、今後の改善見込み
この5本を駆け足でご紹介しました。
気になった記事があれば、詳しい内容や元の記事へのリンクはショーノートにまとめてありますので、そちらからぜひチェックしてみてください。
この番組「zenncast」では、感想や「こんなテーマ取り上げてほしい!」といったリクエストもお待ちしています。ラジオネームをつけて、気軽に送ってもらえると嬉しいです。
それでは、そろそろお別れの時間です。
今日も良い一日をお過ごしください。お相手はマイクでした。
また次回のzenncastでお会いしましょう。バイバイ。