Mitsuyuki.Shiiba

フィード

記事のアイキャッチ画像
Zodのz.discriminatedUnionは結局、識別プロパティの指定が必要な仕様に戻ってた
Mitsuyuki.Shiiba
以前にZod v4リリース前のz.discriminatedUnionを見てた。このときはまだv4リリース前のベータ版だった。そういえば、ドキュメント修正のIssueあげたことをすっかり忘れてたなぁ。どうなったんだろう?って思って見てみたら、最終的に仕様が変わってた。あら。No longer applicable now that z.discriminatedUnion() now requires you to specify a discriminator (as before): #4402This was one of the issues that led me to refactor the implementation. Thanks for the report.こういう感じかな:z.discriminatedUnion() では(以前と同様に)識別プロパティの指定が必要になったので対応は不要になりました: #4402この部分は実装をリファクタリングすることになった理由の一つです。ご報告ありがとうございます。じゃ、動作確認してみるかZod 4.1.13 で確認識別プロパティは必須に戻ってた識別プロパティを指定していないとコンパイルエラーになった。つまり、v3までと同じ感覚で使えばよさそうね。この方が挙動が分かりやすくていいよね。import { z } from 'zod';// 最初の引数の 'kind' の部分は必須。指定してないとコンパイルエラーになる。const MyUnion = z.discriminatedUnion('kind', [ z.object({ kind: z.literal('square'), size: z.number(), }), z.object({ kind: z.literal('rectangle'), width: z.number(), height: z.number(), }),]);const result = MyUnion.safeParse({ kind: 'rectangle', width: 5, height: 10,});console.log(result.data);Issueはこれ。識別プロパティがないと問題になるケースがあったから、結局必須に戻したと。識別プロパティを指定
12日前
記事のアイキャッチ画像
Honoを使ってイベント型のシステムを作ってみた
Mitsuyuki.Shiiba
この記事は Hono - Qiita Advent Calendar 2025 - Qiita の12月3日の記事です!ということで、Honoを使ってこういうシステムを勉強用につくってみた。仕様を考え始めてから、トータルで5,6時間くらいかなぁ。便利な世の中になったものですね。イベントをごにょごにょするシステム最近(ということもないか、この10年ぐらいずっと)、イベントを扱う設計に興味がある。でも、イベントソーシングは開発自体が難しいし、なんとかがんばって開発してサービスインできたとしてもその後の保守や運用をできる気がしない。もうちょっと現実的な落とし所がないかなぁって思ってるところで、ステートソーシングにイベント発行をくっつけるようなOutboxパターンってどんな感じになるんだろう?ちょっと手触りを知っておきたいな、という気持ちになったので、Outboxパターンでイベントを扱うシステムを作ってみた。参照:Pattern: Event sourcingPattern: Transactional outbox構成┌─────────────┐ ┌─────────────┐│ Customer │ │ Kitchen ││ Screen │ │ Screen │└──────┬──────┘ └──────┬──────┘ │ │ │ SSE Events │ ▼ ▼┌──────────────────────────────────┐│ SSE Server │◄──── NATS Subscribe└──────────────────────────────────┘┌──────────────────────────────────┐│ API Server ││ ┌─────────┐ ┌───────────────┐ ││ │ Orders │ │ Event Outbox │ ││ └─────────┘ └───────────────┘ │└──────────────────────────────────┘ │ │ │ ▼ │ ┌───────────────┐ │ │ Poller │───► NATS Publish │ └───────────────┘ ▼┌──────────────────────────────────┐│
24日前
記事のアイキャッチ画像
ESLintとtsdownをバージョンアップしたメモ
Mitsuyuki.Shiiba
どっちも自分が影響を受ける変更が入ってた。ESLint1年前にFlat Config + TypeScriptについて勉強してメモを残していた。typescript-eslintとFlat Config - Mitsuyuki.Shiibaそのあと全然チェックしてなかったけど、2025年3月にみんなの意見を取り入れて更新が入ってた。ESLint v9.22.0 released - ESLint - Pluggable JavaScript LinterEvolving flat config with extends - ESLint - Pluggable JavaScript Linter内容を2つに分けると(1) defineConfigが導入された型安全に設定を扱えるようになった設定をフラットに展開するから ... で展開しなくてよくなったextends は、なくしたけどやっぱりある方がいいってなって再導入したtseslint.config でやってたことが defineConfig として導入された形だね。ということで tseslint.config は deprecated になった。(2) globalIgnoresが導入されたignores は書き方によってグローバルかローカルかが決まる仕様で分かりにくいなぁって思ってたけど、そういう声が多かったみたいで明示的にグローバルと指定できる関数が用意された。tsdownKoriではtsdownを使ってるんだけど、バージョンを最新にあげたらビルドが通らなくなって「はて?」ってなって、みてみたらESMの出力が .d.mts と .mjs になってた。feat!: defaults `fixedExtension` to true when platform is `node` by sxzz · Pull Request #517 · rolldown/tsdown · GitHub.mjs じゃなくて .js を使いたいなと思ったので tsdown.config.js に fixedExtension: false を設定しておいた。参照: https://tsdown.dev/reference/api/Interface.InlineConfig#fixedextension
1ヶ月前
記事のアイキャッチ画像
続・Koriのパフォーマンスをチェックして今度は納得した
Mitsuyuki.Shiiba
↓この記事の後半のパフォーマンスチェック部分の続き。なんか違和感がある結果だなぁって思ってて、もう少し詳しくみていったら今度はわりと納得できる結果になった。Validationの違和感Koriはバリデーターによってパフォーマンスが大きく変わることはないのに、Honoは違っていてValibotのパフォーマンスがいいのが気になっていた。KoriとHonoでそんなに傾向に違いがでるものなのかなぁ?って。なんでだろう?Koriのバグなのかな?と思って見てみても正しくうごいていそう。と、ふと、Valibotのエンドポイントだけにしてみたら結果が変わった。元々はこういうふうに1つのappにすべてのエンドポイントを登録して、それぞれのエンドポイントに順番に負荷をかけて結果を見ていた。(このコードはHonoの部分)app.get('/', (c) => c.text('Hi')) .get('/id/:id', (c) => { c.header('x-powered-by', 'benchmark') return c.text(`${c.req.param('id')} ${c.req.query('name')}`) }) .post('/json', (c) => c.req.json().then((body) => c.json(body))) .post('/validate-zod', sValidator('json', zodSchema), (c) => c.json(c.req.valid('json')) ) .post('/validate-valibot', sValidator('json', valibotSchema), (c) => c.json(c.req.valid('json')) ) .post('/validate-arktype', sValidator('json', arktypeSchema), (c) => c.json(c.req.valid('json')) )https://github.com/bufferings/bun-http-framework-benchmark/blob/3d9a10d4cd3844f1eef221efeb9d618157ebbf11/src/bun/hono.ts#L28-L42それを1
2ヶ月前
記事のアイキャッチ画像
Koriを作って、Hono Confに参加して、Koriのパフォーマンスを見て、へーってなった
Mitsuyuki.Shiiba
2025-11-09 追記この記事の後半で触れているパフォーマンスチェックの結果に違和感があったので、もういちどチェックしました。2025-10-25の本記事よりも、2025-11-09の下記の記事の方が正しい結果になったと考えていますので、パフォーマンスチェックについては下記の記事の方を参考にしてください。追記ここまでKori(氷)というTSのウェブアプリケーションフレームワークを趣味で作っている話を以前に書いた。どっかでいったんブログに書いて休憩しようと思ったので、書くことにした。というつもりだったけど、公表してしまうと「ちゃんとしなきゃ」という気持ちになってしまって、結局そこからの方がもっと真面目に開発をしてしまっている。Songmuさんに声をかけてもらってお話できたりもしたので、開発して良かったなって気持ち。ゆーすけさんにも拾ってもらって、うぉーってなったりしてた。HonoをラップしたKori爆誕! https://t.co/aBhmR91wHP— Yusuke Wada (@yusukebe) August 4, 2025 実装してるときは、わりといい感じだなーって思ってた部分も、テストを書いたりTSDocを書いたり、利用側のコードを書いてみたりしていると、これではダメだーってなったりして、根本的な設計から見直すことにして終わりが見えないなーってなりながら楽しんでた。ブログを見てくれたKanonさんからStandard Schemaってものを教えてもらって、なるほどなーって対応を入れたりしてみた。発信すると自分が知らないことを教えてもらったりして広がっていくよね。ありがたい。ZodではなくStandard Schema準拠ならめちゃ嬉しいけど、Hono依存があるならZod依存もアリか— Kanon (@ysknsid25) August 4, 2025 Hono Confそんなときに会社で「Hono Confでスポンサーセッションをやらせてもらえることになりました!」って話があって、Honoまわりで何か話せたらいいなーって思って「会社は全然関係ないですけどKoriの話をしてもいいですか?内部でHonoを使ってるので!」って言ったら、941さんから「いいですね!」って言ってもらったので喋ることにした。Hono Conf参加者のみなさんにとって無駄な時間になら
2ヶ月前
記事のアイキャッチ画像
どのくらい生成AIに任せているかをあらわす指標
Mitsuyuki.Shiiba
fukabori.fmのtwadaさん回、面白いなー分かるなーって思いながら聞いて、今の自分の頭の中を書きだしてみようと思ったので書いておく。どのくらい生成AIに任せているかをあらわす指標どのくらい生成AIに任せているかをあらわす指標は、こうかなぁと僕は思っている。「生成されたコードを自分が読んでいない割合」どれだけたくさん生成AIにコードを書いてもらっていたとしても、生成されたコードを自分が全部読んで理解している場合は、主導権は自分にある。逆に、生成されたコードを全く読んでいなければ生成AIに主導権がある。右のほうが生産性は上げやすいAIにどれだけたくさんコードを生成してもらったとしても、全部読んで理解しないといけないなら人間がボトルネックになる。右側にいけばいくほど、生成AIに任せられるので生産性は上げやすい。ただ、右側にいけばいくほど自分がコードを理解していないし、構造に手を入れてもいないので、保守や運用や改修は難しくなる。単に右に動かしてコードを読まなくなっただけだと、数カ月後〜1年後には大変なことになっていそう(そしてそういう事態は今後発生しそうだよなとは思っている)。全部が一箇所に当てはまるわけではないところで、AIを使って開発をするときに、全部が一箇所に当てはまるわけではない。ものによって読む割合が変わる。僕はプロダクションコードはAIをあまり使わずに自分で書くことが多いし、AIを使ったとしても全部読んで理解する。つまり一番左になる。逆に、開発中にだけ使うような自分用ツールや、使い捨てのプロトタイプは中身を全く読まずに右端で使ったりする。自動テストはわりとしっかり見るけど、プロダクションコードほどではないし、ボイラープレートなコードの場合はコードよりもテストの方をしっかりチェックしているかもしれない。ドキュメントやREADMEは、ざっと見てOKだったらOKにしちゃうかな。どれくらい右に寄せられるかで生産性が変わるので、例えば自動テストで守られた部分に対する実装の変更依頼などをたくさんAIにお願いできるような状況をつくれば生産性は上がるんだろうなぁと思ったりする。どうやったら右に寄せられるかをみんな試行錯誤してるんじゃないかなと思っている。AIツールはどこ?Cursor/Claude Code/Devinなどが得意な領域はあるよなと思っている。Curso
3ヶ月前
記事のアイキャッチ画像
境界は超えないように気をつけるより超えて体感するのがいい(生成AIコーディング)
Mitsuyuki.Shiiba
会社で生成AIを使って仕事をしつつ、家でも個人でCursorのUltra Planを契約して使い倒してみている別に会社で使ってるなら家で契約せんでもよくない?って気もするし、自分のまわりの人に「家でも使おうよ!」と勧めるつもりも全然ないんだけどじゃあ、なんで自分は家でも使ってるかって言うと「境界を知りたい」という気持ち。「ここを踏み越えたらおいしくない」みたいな境界仕事だとどうしても、そういう境界がありそうだなって時点で踏みとどまって、踏み超えないようにしてしまう。でも、プライベートだと「構わん、行け」が気兼ねなくできるあれ?ここに境界がありそうに思ってたけど、もうちょっと踏み込めるぞ?まだいける?あー!ここに境界があったかー!へー!みたいなのgit checkoutで作業内容を消されたり、ファイルを削除されたり、全然関係ないファイルを編集し始めたり、これ自分でやったら1分で終わることを10分ぐらいかけてやってるやんってなったり、gpt-5と口論したり(時間の無駄)それ以外にも、全然関係なさそうなところをうろうろしてみたり、ヤクの毛刈りをはじめたり、しばらく触るのをやめてみたりとか、いろいろできるのがプライベートのいいところだよなぁって思うおかげで仕事のときは「ここに境界があるんだよな」って考えながらシュシュっと進められていいプライベートで使ってるのはそういう「壊してもいいおもちゃで遊ぶ」って理由が半分で、あとは単純に「おもしろいなー」って気持ちが9割だな今日は大吉祥寺.pmに行って楽しかった
4ヶ月前
記事のアイキャッチ画像
チームの問題の原因は外側にあることが多いよなぁ。「チームの力で組織を動かす」を読んだ。
Mitsuyuki.Shiiba
「技術的負債をなんとか減らさなきゃ!」とがんばっているのに、なんかうまくいかないってケースをちょくちょく見る。忙しくて時間が取れないとか、少し改善を進めている間に別の機能追加によってまた負債を抱えてしまうとか。僕はこの10年ぐらい、どうやったらもっとうまく開発できるかなぁって考えながら過ごしている。よりうまく開発をするためには、開発チームの内側を良くするのはもちろんだけど、それ以上に、開発チーム自体を組織の中でどのように設計するかがとても重要だよなと思っている。8月25日に発売です!最近もそんなことを考えながら過ごしていたところ @mtx2s さんから「チームの力で組織を動かす」をいただいた。明日(8/25)発売です!めちゃ面白かった。結構ボリュームがあるので、最初ザーッと読んで、次に、気になったところを中心に読み込んでいった。図がたくさんあって分かりやすいのも良かった!組織やチームについて考えているエンジニアリングマネージャやテックリードにはぜひ読んでほしい。目の前の問題をなんとかするための対応が別の問題を引き起こしてしまうようなアンチパターンや、じゃあどうすればいいのかという組織設計の原則がまとめられていて、とても参考になる。技術的負債に日々向き合っているエンジニアのみんなも「あぁ、このアンチパターン分かる!ここから問題が発生しているのかーたしかになー」といろいろ考えさせられて面白いのでぜひどうぞ。最近は開発が遅い?「以前はもっと少人数で勢いよく開発が進んでいたのに、今はこれだけの人数がいるのにどうして進みが遅いの?」みたいな声を聞いたりして「以前に少人数で勢いよく開発を進めていたから、今大変なんですよね」という気持ちになったりする。それは、少人数で勢いよく開発を進めていたことを否定する気持ちではない。その時代があったから今があると思っている。ただ、そこで意識的に・無意識的に蓄積された技術的負債と、プロダクトや組織の拡大が絡まって、どんどん開発の難易度は上がっていく。だから、その難しさに対応するための組織設計がとても重要になってくる。「組織設計のひずみによって生じた問題は現場だけでは解決できない」最初の方に出てくる言葉。これ好き。組織設計のひずみによって生じた問題は現場だけでは解決できない。そうそうそれだよなーという気持ち。エンジニアが対応しないといけなくなる場
4ヶ月前
記事のアイキャッチ画像
Kori (氷) というTypeScript用のウェブアプリケーションフレームワークをCursorたちと一緒に作ってみている
Mitsuyuki.Shiiba
とりあえずコンセプトは動きそうだなぁってくらいで、ちゃんと動くことも確認してないし、テストも書いてないし、まだまだやることはたくさんあるんだけど、どっかでいったんブログに書いて休憩しようと思ったので、書くことにした。年内である程度動くところまで持っていけたらいいな。KoriKori (英語版を作ってその翻訳をCursorにお願いしたのでそういう感じの日本語になってます)特徴TypeScriptの型安全さをわりといっぱい活かしてコードを書ける。スキーマを定義すると、そのスキーマにしたがってバリデーションが実行されて、その結果を型安全に扱える。その同じスキーマをOpenAPIのスキーマとしても利用できる。スキーマの実装としては、とりあえずZod v4に対応しておいた。たとえばこんな感じで定義するとconst UserSchema = z.object({ name: z.string().min(1).max(100).meta({ description: 'User name' }), age: z.number().int().min(0).meta({ description: 'User age' }),});app.post('/users', { pluginMetadata: openApiMeta({ summary: 'Create user', description: 'Create a new user with validation', }), requestSchema: zodRequestSchema({ body: UserSchema, }), handler: (ctx) => { const { name, age } = ctx.req.validatedBody(); const newUser = createUser(name, age); return ctx.res.status(HttpStatus.CREATED).json(newUser); },});ScalarというUIでOpenAPIのドキュメントを確認できる。Scalarは知らなかったんだけど「Swagger UI以外にもなんかある?」ってCursorに聞いたら教えてくれた。Scalar - Document, Test & Discover APIs
5ヶ月前
記事のアイキャッチ画像
CursorのプランをPro($20)からPro+プラン($60)に変更した
Mitsuyuki.Shiiba
最近「説明が足りなくてごめんね」って記事(Clarifying Our Pricing | Cursor - The AI Code Editor)がでたりして話題のCursorの料金プラン。僕はプライベートではProプラン($20)を使っていて、新料金プランのレートリミットモードはわりと気に入っていた。でも、けっこうすぐに$20ぶん使い切って、(たぶん)月間のレートリミットに達してしまっていた。こっからは従量課金かーって気持ちで$100リミットとかにして使っていた。プランのアップグレードも考えてみたけど、Proの上位プランだとUltraプラン($200)だから・・・んー、そこまではちょっとなぁ・・・って思っていた。そんな中でふとドキュメント(Cursor – Models & Pricing)を眺めてたら「あれ?Pro+プラン($60)なんてあるんだ?これくらいならちょうどいいかも?」って思って、Pricingページ(Pricing | Cursor - The AI Code Editor)を探してみても見つからない。「あれー?ドキュメントには書いたけどまだ公開されていない」ってことなのかなぁはやくくるといいなぁって過ごしてた。ら、何かで「僕はPro+プランで$60払ってるんだけどね(英語)」みたいなコメントを見かけて「あれ?もうあるの?」ってなって調べてみつけた。https://www.reddit.com/r/cursor/comments/1lqig05/how_to_actually_upgrade_to_pro/(意訳)リミットに達した人に「Pro+にアップグレードする」を出してるよ!従量課金を設定してる人には出てこないからいったんオフにしたら出てくるよ!おーい!!!ほんまや。そんなん気づかんわー!!!ってことで無事Pro+プランに変更できた。からの、Proプランの残りの日数分は日割りで返金された。Redditには「近い内にDashboardにも載せるね。でも大多数は$20のProで大丈夫なはずだから、Pro+はちょっと奥の方に置いとくつもり」って感じのコメントがあるので、近い内に設定ページかPricingページあたりに出てきそう。ということでしばらくPro+プランで遊ぶー。$20のProで10日くらいもったから、$60のPro+だったら3倍使えるので
6ヶ月前
記事のアイキャッチ画像
CursorのBackground Agentで遊んでいてわりと面白い
Mitsuyuki.Shiiba
「最近のおうちコーディングは、CursorのBackground Agentで遊んでいてわりと面白いよ」って会社の同僚に言ったので、簡単にメモだけ残しておくことにする。前半でBackground Agentのことを、後半で僕がどう使ってみてるかを簡単に紹介する。Cursor Background Agent?Cursorをふつうに使うと、AIとチャットしながらコーディングしている感じになる。それはそれでいいんだけど、それとは別で、Cursorにはリモート環境で非同期でタスクを実行してくれる Background Agent という機能がある。ローカル環境とは別のところで実行されるので、手元では別の作業を続けられて便利。https://docs.cursor.com/background-agent準備GitHubとの連携をしておく必要がある。あと、Slack連携もしておくと便利。依頼 on CursorCursor上で ⌘E を押すと、Background Agent用のモーダルが開くので、そこに依頼を書くだけ。そうすると、Background Agentがリモート環境で動き始めて、タスクを進めてくれる。依頼 on Slackもしくは、SlackからCursorにメッセージを送っても、Background Agentが動き始める。依頼 on Webさっき「Background AgentにWebからもアクセスできるようになったよ!」ってアナウンスがあった。タイムリーね。そのWebからも依頼ができる。Cursor is now on your phone and on the web.Spin off dozens of agents and review them later in your editor. pic.twitter.com/y7mOlA9h07— Cursor (@cursor_ai) June 30, 2025 これまでは、依頼の進み具合を見るためにはCursor Appを使う必要があったから、DevinみたいにWebでも確認できたらスマホからもチェックできて便利なのになーって思ってたところだった。Webでも確認できるようになったのは便利そう。PRを作るCursor上でもWeb上でも、Background AgentのビューからPRを作ることができ
6ヶ月前
記事のアイキャッチ画像
Devinにドキュメントを生成してもらう実験
Mitsuyuki.Shiiba
Devinにお願いしてソースコードからドキュメントを生成してもらえると面白そうなので実験してみた。Devin Wiki や Deep Wiki もあるんだけど、それとは別に自分で指示を出してコントロールできるのもいいかなという気持ち。どうせ作るなら自分がドキュメントを読みたいやつがいいなぁと思って、ecspresso が好きだから、ecspressoのソースコードからドキュメントを生成してみることにした。軽い気持ちでやってみたら、思ってたより苦戦した。すごくいい感じにできたわけじゃないので「この記事をめちゃ信じる!」んじゃなくて「へー、ちょっと参考にしとこっか」くらいが良いと思う。勢いで書かないと書き終わらなさそうだったので、勢いでざーっと書いた。ので長い。できあがったものできあがったものを最初に書いておく。わりと気に入ってる。ただ、生成するたびに色々変わるので、雰囲気で参照することにする。注意:このドキュメントが正しいかどうかは確認できていないです!https://bufferings.github.io/devin-docs/ecspresso/注意:今後色々触っていくうちに上記のドキュメントは見えなくなるかもしれないDevin?Devin https://devin.ai/お願いすると勝手に動いてPRを作ってくれるAIエージェント。個人では Core (Pay as you go) プランを契約している。使った分だけ払うやつ。やりたいことecspressoのソースコードを元にドキュメントを生成して、それをGitHub Pagesで見られるようにしたい。最初はecspressoをフォークしてごにょごにょしてたけど、それよりもドキュメント生成用のリポジトリを作るほうがいいかも?と思ってそうした。そのリポジトリからecspressoをチェックアウトしてきてドキュメントを生成する。https://github.com/bufferings/devin-docsGitHub Actionsをポチると、Devinにドキュメント更新を依頼するDevinが最新のソースコードを読んでドキュメントを生成してPRを出す僕がPRを手でマージするmainブランチが更新されると、GitHub Pagesが最新のドキュメントに更新されるメインは手順2のプロンプトなので、それ以外を先にさらっ
7ヶ月前
記事のアイキャッチ画像
Zod v4でz.discriminatedUnionはどうなる予定なんだっけ?を見ておいた
Mitsuyuki.Shiiba
2025-12-14 追記正式版リリースの前にdiscriminatedUnionまわりの仕様変更が入っていました。この記事の内容から変わってるので↓こちらの記事も合わせてご確認ください!追記ここまで「数日前に↓のIssueがクローズされたんだよねー。Zod v4が関係してそう。知らんけど」って会社の同僚と喋ったので、ちゃんと知っておくかーって気持ちになった。※ 本記事はzod@4.0.0-beta.20250505T195954時点の挙動をもとにしています。正式版で変更される可能性があります。最初にまとめz.switchが追加されるとか、z.discriminatedUnionが非推奨になるとかの話はなくなった様子Zod v4 ではz.discriminatedUnionに識別プロパティを渡さないようになった(渡しても無視される)共通の識別プロパティを持たずに、各オプションがそれぞれリテラルかenum(ネスト可)のプロパティを持つだけでよくなったZod v3までのz.unionとz.discriminatedUnionIssueの内容を説明する前に、現行であるZod v3のz.unionとz.discriminatedUnionについておさらいしておく。z.unionDefining schemas | Zodどちらかの型だよという定義。const stringOrNumber = z.union([z.string(), z.number()]);パースするときは、前から順番にチェックして最初に成功したものを返す。パースに失敗した場合は、すべての型に対する失敗内容を返す。例えば、こういうスキーマの場合(あとでDiscriminated Unionと比較するため、わざとそういう定義にしてる)import { z } from 'zod';const MyUnion = z.union([ z.object({ kind: z.literal('square'), size: z.number(), }), z.object({ kind: z.literal('rectangle'), width: z.number(), height: z.number(), }),]);const result = MyUnion.safeParse({ kind: 'cir
8ヶ月前
記事のアイキャッチ画像
旗振りをするときに考えてること
Mitsuyuki.Shiiba
エンジニアとしてチームをリードするときよりも、もう少し広めの範囲をリードするときの話。1 つのチームをエンジニアとしてリードするときには、ぐいっと引っ張ったり、やって見せたり、そばでペアプロをしたり、直接話をしたり、そういう直接のコミュニケーションでリードできるし、そうしたい。でも、複数チームの方向性を決めるような旗振りをするときには、そういう直接のコミュニケーションは難しくなる。それに 1 つのチームを見ているときよりももう少し遠くを見ることが多い。そんなときに意識しているのは「自分が考えてることを見えるようにすること」。考えてることが見えないと考えてることが見えないタイプのリーダーは、メンバーとしては動きにくいなって思ってしまう。責任感が強かったり、自分がリーダーとしてしっかりしなきゃ!と頑張ってたりするから、というのが多いかも。で、いろいろ悩んで、最終的に決まってから「こういう方針に決めました」と伝えてくれる。でも、やることが決まってから渡されると、メンバーには考える余地がない。一緒に課題に向き合ってるのにね。まぁ、言われたことはやりますけど、ってなってしまう。経緯が分からないから納得しづらかったりもする。みんなを頼りたいそれよりも僕は、みんなを頼りたいなと思っている。僕一人でなんとかなることなんてたかがしれてるから、みんなのチカラをできるだけ借りたい。だから、考えている段階で「こういう課題に対して、こうやりたいなと考えて進めているよ」というのをできるだけ見えるようにする。今はフルリモートの場所にいるから、Slackやドキュメントで考えている途中のこともできるだけ見える場所に書いている。そうすると見てる方も「なるほどね。じゃそろそろこういう話になりそうね」って準備ができたり「あの話、進んでるのかなぁ?」って思わずに「あの話はいまこういうステータスか・・・なるほどね」って待ってくれたり「その課題には、こういうやり方もありだと思うよ」ってアドバイスをくれたり、あとは「そういう考えで進めてたのに、この話でブロックされてこうなったのか」って納得できたりする。そんな風に過程を見えるようにして、みんなが動きやすい状態をつくりたい。んで、一緒に動きたい。諸刃の剣だったりもするただ、これは諸刃の剣だったりもする。みんなを頼ってばっかりであの人何もしてないやんって思われたり、「
8ヶ月前
記事のアイキャッチ画像
DenoでMCPサーバーを作って遊んだ
Mitsuyuki.Shiiba
数日前に↓を読んで、なるほどー!ってなったので、なんか作ってみるかと思いMCPサーバー自作入門なんとなく、npxで実行するより、実行可能ファイルにしてみたいなと思って、Denoに初挑戦。↓こちらを参考にして作ってみたDeno で RooCode 用にローカルMCPサーバーをさっと作る↓こうなったhttps://github.com/bufferings/mcp-servers/tree/main/hello実行可能ファイルはこんな感じで作って❯ deno compile --output dist/hello hello/index.tsCursorでそのファイルを設定して{ "mcpServers": { "hello": { "command": "~/mcp-servers/hello", "args": [], "env": {} } }}有効化して文字列の長さを聞いてみると、使ってくれるGitHub ActionsでビルドしてGitHub Pagesにでもあげとくかと思ってCursorにお願いしたらワークフローファイルを書いてくれた。ありがと。実行ファイルが70MBあるのはちょっと大きいなぁとは思うけど、自分用には便利(他の人がビルドしたバイナリは実行しないほうがいいと思う)。これで、適当に遊びで色々作る環境ができたー。Cursorはまだtoolsしかサポートしてないってことなので、toolsで遊んでみたらいいかなー。
8ヶ月前
記事のアイキャッチ画像
GitHub ActionsでAI呼び出し
Mitsuyuki.Shiiba
すごく久しぶりにブログを書く。3ヶ月ぶりくらい?↓この記事を読んで「へー。どういうこと?」ってなったので遊んでみた。「GITHUB_TOKEN でGitHub Modelsを呼び出せるようになったよ!PAT(Personal Access Tokens)はもう使わなくていいよ!」って書いてある。そもそもGitHub Modelsを知らんかったけど、GitHub Copilotとかで使ってるAIのモデルたちのことかな。やってみたGitHub ActionsでAIの呼び出しができるよってことだろうなと思って適当に作ってみた。「今日の運勢を教えて!」って投げるだけのGitHub Actions。「今日は新しいことにチャレンジする絶好の日!」ふむふむー。コードgithub.comname: GitHub Models API Exampleon: workflow_dispatch:permissions: models: readjobs: call-model: runs-on: ubuntu-latest steps: - name: Call AI model id: model-call env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PROMPT: "今日の運勢を教えて!" run: | # API呼び出しと生のレスポンスの保存 RAW_RESPONSE=$(curl "https://models.github.ai/inference/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -d '{ "messages": [ { "role": "user", "content": "'"$PROMPT"'" } ], "model": "openai/gpt-4o" }') # 生のレスポンスを一時ファイルに保存 echo "$RAW_RESPONSE" > raw_response.json # レスポンスからコンテンツ部分を抽出 CONTENT=$(echo "$RAW_RESPONSE" | jq -r '.choices[0].message.conten
8ヶ月前
記事のアイキャッチ画像
HonoのZod OpenAPIを読んでたらWebStormのCPUとメモリを消費しまくってたのをJetBrainsにレポートしたらすぐに解決方法を教えてもらってニッコリなメモ
Mitsuyuki.Shiiba
HonoとZodとOpenAPIまわりが気になって、Zod OpenAPIのコードをWebStormで読んでたら、WebStormとNode.jsのプロセスがCPUをたくさん消費して、動きが遅くなって困った。メモリもWebStormに割り当てたぶんを使い切っちゃうよーって警告が出てきてた。なんの気なしにそんなことをつぶやいてたらJetBrainsの@niklas_wortmannがコメントをくれた。Did you happen to create a YouTrack ticket with some CPU or Memory snapshots? Would love to further look into this— Jan-Niklas Wortmann (@niklas_wortmann) January 18, 2025 ので、たしかにレポート送るのいいか、と思ってYouTrackにIssueを報告した。Issue自体は僕(とJetBrainsの人)にしか見えないっぽいんだけど、内容はこんな感じHonoのZod OpenAPIのコードをWebStormで読んでたらCPUとメモリの使用量が多くなっちゃうこのコードで再現する https://github.com/bufferings/20250119_webstorm_cpu_memそしたら早速返事が来て、対応方法として↓をやってみて!って言われてやったら解決した。Help | Change Memory Settings... でメモリを増やして2048MBだったので、4096MBに増やした。まぁたしかに2GBは少ないなTools | Internal Action | Registryから typescript.service.node.arguments を探して --max-old-space-size=3072 を設定してみてInternal Action が見つからなかったので Find Action... から Registry を検索して設定したSettings | Languages and Frameworks | TypeScript から“Use types from server” をオンにして“Show project errors” をオフにしてみてなんでこれで解決したのかは
1年前
記事のアイキャッチ画像
Honoのルーティングの分割を思いつくままに実験
Mitsuyuki.Shiiba
GroupingHonoにはGroupingという機能がある。僕はルーティングを複数のファイルに分けて書きたいタイプなので、たぶんこれを使うだろうなと思っている。https://hono.dev/docs/api/routing#grouping実験環境Node.jsが好きなのでNode.jsで動かしている。hono: 4.6.16@hono/node-server: 1.13.7node.js: 23.6.0分割に対するHono的おすすめはappまるごとルーティングを分割して書こうと思うと、まず最初に思いつくのはhandler関数を別のファイルに定義することかなと思う。で、それをメインのルーターのところでパスにマッピングする。でも、Hono的なおすすめはそうじゃない。handler関数ではなくappインスタンスを別のファイルに定義する。それによって、型を気にしなくてもHonoがいい感じに推論してくれる。https://hono.dev/docs/guides/best-practicesappインスタンスを別のファイルに定義する?たとえば、こんなパスを定義したいとき/users/users/:id/books/books/:idこんな風にUser用とBook用を分割できるimport { Hono } from 'hono';const userApp = new Hono();userApp.get('/', (c) => { return c.text('users');});userApp.get('/:id', (c) => { const id = c.req.param('id'); return c.text('Get User: ' + id);});export { userApp };import { Hono } from 'hono';const bookApp = new Hono();bookApp.get('/', (c) => { return c.text('books');});bookApp.get('/:id', (c) => { const id = c.req.param('id'); return c.text('Get Book: ' + id);});export { bookApp };そして、それらをメインap
1年前
記事のアイキャッチ画像
TypeScriptのBranded TypeとZodの.brand
Mitsuyuki.Shiiba
Branded Typeについては、もういろんなところで触れられているから、わざわざ書かなくてもいいよなぁという気持ちがありつつ。でも、せっかく頭の整理をしたから、来月の自分用にまとめておくくらいはやっておこうか、という気持ちになったのでメモを残しておく。それとZodの.brand。やりたいこと次の2つの型に対する値を間違えて渡したときに、TypeScriptの型検査でエラーになってほしい。type UserId = number;type BookId = number;TSの型システムはStructural Subtypingを採用しているので、構造が同じだったら部分型として扱われる。だから、↓こんな風にUserIdを引数で受け取る関数にBookIdの値を渡してもエラーにならない。どちらも同じnumber型だから。const getUser = (id: UserId) => ({ id, name: 'SHIIBA' }); // 例なので適当const bookId: BookId = 1;const user = getUser(bookId);// UserId型の引数に対してBookId型の変数を渡してもエラーにならない先に言っておくと先に言っておくと、Branded Typeをいたるところで使いたいとは思っていない。それなら最初からJavaとかを使えばいいやん、と思う(フロントエンドTSじゃなくてサーバーサイドTSの頭で喋っている)。TSを使うからにはTSの良さ・柔軟さを大切にしたいので、普通に書いても大丈夫な部分はできるだけ普通に書きたい。でも「ここは型で守っておきたいな」という部分にはBranded Typeという選択肢もあるよなって、引き出しに入れておこうという気持ち。Branded TypeということでUserIdとBookIdを区別するのにBranded Typeと呼ばれる手法が使える。↓こんな感じでマーカーとなる型をIntersection Typeでくっつけるtype UserId = number & { __brand: 'UserId' };type BookId = number & { __brand: 'BookId' };そうするとUserIdとBookIdが異なる構造になるので区別されるようになる。const bookI
1年前
記事のアイキャッチ画像
Zodのドキュメントを読みながら自分用にメモ
Mitsuyuki.Shiiba
基本的なことは大丈夫なのでざっと読んで気になったところだけをメモしておく。使ってるときに「そういえばこんな機能がZodにあった気がする」って頭の中で引っかかるように。視点としては、Webアプリケーションのサーバーサイドを作る頭で読んでる。フレームワークを作る頭とかではない。Coercionコンストラクタ関数を噛ませて変換するやつ。z.coerce.string()Literals忘れたりはしないだろうけどメモz.literal("tuna")StringsdatetimeZodStringにくっついてくる。タイムゾーン有無・精度・ローカルとかある。const schema = z.string().datetime({ local: true });schema.parse("2020-01-01T00:00:00"); // passdatetime以外に、dateもtimeもある。日付文字列を扱うやつだから、日付型を扱うやつと頭の中で混乱しないように注意しておきたい。Zod enumsへー。ZodEnumには.enum なんてのがあるんだ。値だけを返す .options もある。.extract と .exclude もある。(使わない) Native enums僕は使わない.optional .nullable .nullishラッピングする関数もあるけどconst schema = z.optional(z.string());↓僕はメソッドチェーンの方を使いそうconst schema = z.string().optional();.nullable() もそう。nullとundefinedを両方許容する .nullish() もある。.unwrap() で中のスキーマを取り出せる。Objects(たぶん使わない) .shapeプロパティのスキーマにアクセスできるDog.shape.name;(たぶん使わない) .keyofキーのZodEnumを生成する。へー。ZodEnumなのか。const keySchema = Dog.keyof();keySchema; // ZodEnum<["name", "age"]>.extendフィールドを追加できる。上書きもできるので注意const DogWithBreed = Dog.extend({ breed:
1年前
記事のアイキャッチ画像
結局iTerm2を単体で使うことにした
Mitsuyuki.Shiiba
年末年始にGhosttyを触ってみてて、好きだなとは思いつつもメイン使いするのはもうちょっと待ちたいなという気持ちになった。なので、↓以前に書いたみたいにiTerm2 + tmuxに戻すかー!と思って、それならtmuxの使い方をもういっかい勉強するかー!って考えたんだけど。はて?そういえばなんでtmuxを使ってるんだっけ?ってなった。もともとは、踏み台からサーバーに入って作業をするときに切断されないようにするためだったな。ついでに複数台のサーバーに入りやすくて便利だなくらいで使い始めたんだった。もう今じゃ踏み台からサーバーに入ることもほとんどないし、単に複数ペインやタブで操作したいだけならiTerm2だけでよくない?って気持ちになった。それに、もしまた踏み台経由でtmuxを使いたいってなったら、iTerm2のtmuxインテグレーションの機能を使えばiTerm2の使い方でtmuxが使えそうだからそれでいいよな。ということでiTerm2を単体で使うことにした。なにはともあれバージョンアップから。3.5.11へあげる。設定こんな感じになった。わりと気に入ってる。ふだんはドロップダウンウィンドウをホットキーで呼び出して使う。通常のウィンドウで使いたいときもあるので、そういうときはCtrl + Nで新規ウィンドウを作成すると通常のウィンドウで開くようにしている。プロンプトにはStarshipを使っているとはいっても、色々表示されると気になってしまうので、何も出さないようにシンプルにしている。Color SchemeにはDraculaを使ってるこれもずいぶん前から設定してある。ちょこっと色をいじったかもしれない。Shell IntegrationをオンにしたSettings > Profiles > General > Command > Load shell integration automatically にチェックを入れた色々便利機能はありそうなんだけど、そのへんはよく分かってない。コマンドの結果が長い場合でもコマンドが上部に固定されるのが好きでそれだけのためにオンにした。Clicking on a command selects it to restrict Find and FilterをオフにしたSettings > General > Selection > C
1年前
記事のアイキャッチ画像
Ghostty使っていこうかな
Mitsuyuki.Shiiba
2025-01-07 追記年末年始に触ってみてて、いまはまだiTerm2+tmuxのままにしておこうと思ったのだった。検索ができなかったり、ちょこちょこ気になる動きをする部分があったりするから。追記ここまで今朝リリースされたなんとなく楽しみにしてたやつ。わーい。https://t.co/39Xj39wheA 👻 pic.twitter.com/PH0qejFB4z— Mitchell Hashimoto (@mitchellh) December 26, 2024 使ってみようと思ったちょっと触った感じ、なんとなく心地良いなと思ったので使ってみることにする。僕は今 iTerm2 + tmux なんだけど、これを機に tmux もやめてみて Ghostty だけを使ってみようかなと思っている。Ghostty + tmux でもいいよなとは思ったんだけど、せっかくなら Ghostty の機能をちゃんと使ってみるかなという気持ち。tmux を全然使いこなせていないから大丈夫(?)。テーマやフォントデフォルトのままで好きな感じなので、変更せずに使う。ちなみに、デフォルトフォントは JetBrains Mono。タブやビューの分割や移動これぐらいで自分は大丈夫そう。⌘Tで新規タブ作成。⇧⌘[や⇧⌘]でタブを移動⌘Dで横にスプリット、⇧⌘Dで縦にスプリット。⌘[や⌘]でスプリットを移動⇧⌘↵でスプリットのズームをトグルQuick Terminal僕はGuakeのようなドロップダウンウィンドウが好きなんだけど、GhosttyにはQuick Terminalという機能があって、まさにこれ。グローバルのホットキーを設定してF12で表示されるようにしておいた。設定keybind = global:f12=toggle_quick_terminalkeybind = f11=toggle_fullscreenglobalはアプリケーションにフォーカスがあたってなくてもそのショートカットを使えるようにする設定。Quick Terminalではタブが使えないんー。タブは使いたいかなー。じゃ、ドロップダウンウィンドウをやめてみるかこれを機にドロップダウンウィンドウ自体もやめてみてもいいかもしれない。ホットキーを押したら画面がでてくればいいか。と思ってtoggle-visibilityをF12...
1年前
記事のアイキャッチ画像
午前中に読み始めたら午後には設計が上達してしまった! - 『Tidy First?』を読んだ
Mitsuyuki.Shiiba
私の目標は、読者が午前中に本書を読み始めたら、午後には設計が上達していることだ。本当にそのとおりだった。読んでる途中で既に自分の設計に対する考えが良い方向に変わってると感じた。とても良かった。おすすめです。『Tidy First?』をいただいて読んだ。昨日(2024年12月25日)発売。英語版が2023年11月28日発売だから、たった1年で日本語版が出たということだな。うれしい!はやい!ありがたい!ソフトウェア設計に焦点を当てたシリーズの最初の1冊ということで、サブタイトルに「個人で実践する経験主義的ソフトウェア設計」とあるように、1人でできる種類のソフトウェア設計について書かれている。続刊ではチームについての話になる予定のようで、それも今から楽しみ。2周読んだなんとなく2周読もうと思ってそうした。1周目は細かい部分は気にせずにざーっと1,2時間くらいで読んだ。全体的にどういう話があるかを把握してから、2周目に突入。2周目は1行1行しっかり理解しながら数日かけて読んだ。1周目:前半が面白かった1周目は、第1部が分かりやすくて面白いなと思いながら読んでた。第1部では、ガード節や説明変数など、ほんとに個人で取り組める整頓が紹介されている。だいたいどこかで目にしたことがあるかなとは思う。そういう、一人で10分から1時間ぐらいあれば修正できてしまうような整頓によってコードを読みやすしていくのはとてもいい。後半は内容が少し難しいなと思いながら読んだ。デッドコード消そう。以上。笑った。それはそう。— SHIIBA Mitsuyuki (@bufferings) December 17, 2024 2周目:後半が面白かった1周目では整頓の具体的な一手を楽しんでたけど、2周目は一歩下がって眺めてみた。「整頓はいいよね。やりたい。でも、実際の仕事で難しいのはいつ・どんな風に整頓へ取り組むかなんだよなぁ」と思ってたら、第2部がそれについての話だった(←気づくの遅い)ので「わー!そういう流れだったのかー!面白いー!」ってなって読んだ。第3部もさらに面白かった。第2部:振る舞いの変更、構造の変更第2部では、個人レベルでどのように日々の開発の中に整頓を取り入れるか、について紹介されている。通常の開発では、システムに振る舞いの変更を加える。それに対して整頓は「コードの構造の変更」をする。この構
1年前
記事のアイキャッチ画像
typescript-eslintとFlat Config
Mitsuyuki.Shiiba
2025-11-22 追記ESLintに変更が入って tseslint.config はdeprecateになってた。2025-11-22 追記ここまでtypescript-eslintのFlat Configについて、自分に今必要そうな部分だけをひととおり確認したので忘れる前にメモを残しておく。前提素のJavaScriptプロジェクトをやることは自分はあまりなさそうなのでTypeScript前提ES Modules前提でいいやと思っているので設定ファイルの拡張子はシンプルに .js にするフォーマッターにはESLintのStylisticじゃなくてESLint外のフォーマッター(PrettierやBiome)を使う前提基本の設定https://typescript-eslint.io/getting-started/ の最初に書いてある設定。// @ts-checkimport eslint from '@eslint/js';import tseslint from 'typescript-eslint';export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommended,);この設定の意味**/*.js, **/*.cjs, and **/*.mjs と '**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts' に対してeslint.configs.recommended と tseslint.configs.recommended が適用されるシンプルだね。参照:推奨ルールの実装eslint.configs.recommendedhttps://github.com/eslint/eslint/blob/90c1db9a9676a7e2163158b37aef0b61a37a9820/packages/js/src/configs/eslint-recommended.jstseslint.configs.recommendedhttps://github.com/typescript-eslint/typescript-eslint/blob/a383d5022b81eaf65ce7b0946491444c6eaa
1年前
記事のアイキャッチ画像
NeverThrowのESLintプラグインをフォークしてFlat Configに対応した
Mitsuyuki.Shiiba
ちょっと前にNeverThrowを触ろうとして、そのESLintプラグインがFlat Configに未対応だったから、そこで遊んでしまった話を書いたけど。遊んでしまったついでに、フォークしてFlat Configに対応した。www.npmjs.compnpmとかでインストールして# npmnpm install --save-dev @bufferings/eslint-plugin-neverthrow# pnpmpnpm install --save-dev @bufferings/eslint-plugin-neverthrowこんな感じで設定を書いたらOKimport eslint from '@eslint/js';import tseslint from 'typescript-eslint';import neverthrowPlugin from '@bufferings/eslint-plugin-neverthrow';export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommendedTypeChecked, neverthrowPlugin.configs.recommended, { languageOptions: { parserOptions: { projectService: { allowDefaultProject: ['*.config.*'], }, tsconfigRootDir: import.meta.dirname, }, }, });そうすると、中身を取り出していないResultは怒られる。わーい。利用サンプルはここに置いておいた注意点プラグインの書き方がよくわかっていない。Node.jsのバージョン対応とからへんあやしい。そもそも、Ruleのコアの部分のロジックは読まずに、その周りの対応だけをしたので、ロジックがどうなってるか分かってない。semantic-releaseを雰囲気で使っている。ちゃんといい感じでリリースできるか分かってない。まぁ、自分が個人で使うぶんにはこれでいいかな。ちょこっと改良とかできたら面白いかな。
1年前
記事のアイキャッチ画像
NeverThrowを触ろうと思ったらESLintのFlat Configを触っていた(後編)
Mitsuyuki.Shiiba
の続き。eslint-plugin-neverthrow に手を入れて動くようにしてみるぞー!そして記事を書き終わるぞー!eslint-plugin-neverthrow に手を入れてみるそのままでは動かなさそうだから手を入れて動くようにしてみたい。パッケージを作ればできそうではあるけど、もうちょっと簡単にできないかな?と思って探してみたら、こんな記事を見つけた。ありがたい。srcのとなりにコードを置いて、それを使うようにできるのかー。これなら簡単に eslint-plugin-neverthrow に手を入れられそう。この記事や、ESLintのプラグイン自作ページを見ながら試してみたら、サンプルのプラグインが動いた。この仕組みで eslint-plugin-neverthrow に対して手を入れてみよう。my-plugin という名前でプラグインを作る# プラグイン用のフォルダを作る❯ mkdir my-pluginindex.js はこんな感じ。import {rule} from './must-use-result.js'const plugin = { meta: { name: 'my-plugin', version: '1.0.0', }, rules: { 'must-use-result': rule },}export default pluginそして、must-use-result ルールのファイルをダウンロードしてくる。TypeScriptのファイルだから、tscでjsにコンパイルすればいいか。❯ cd my-plugin❯ curl https://raw.githubusercontent.com/mdbetancourt/eslint-plugin-neverthrow/refs/heads/master/src/rules/must-use-result.ts -O❯ lsindex.js must-use-result.tsで、コンパイルエラーが出てるからまずはそれを解消しよう。import部分のエラーimport部分では↓の3ヶ所がエラーになる。(1) tsutils がない:import { unionTypeParts } from 'tsutils';(2) @typescript-eslint/experimental
1年前
記事のアイキャッチ画像
NeverThrowを触ろうと思ったらESLintのFlat Configを触っていた(中編)
Mitsuyuki.Shiiba
の続き。Flat Configで eslint-plugin-neverthrow を使えるようにするぞー!Flat Config?そもそもFlat Configをよくわかっていないってところからスタート。へー。v9でFlat Configがデフォルトになっていて、v8は2024年10月からEOLなのか。https://eslint.org/version-support/んで、次のv10で旧設定は使えなくなる。↓に「2024年末から2025年頭くらい」って書いてある。もうほんとにすぐだな。https://eslint.org/blog/2023/10/flat-config-rollout-plans/#eslintrc-removed-in-eslint-v10.0.0僕は公式ドキュメントに最初からは入れないタイプなので、心のハードルを下げるために分かりやすそうな記事を読んでみる。↓安心のSajiさん@サイボウズフロントエンド。記事を読みながら思ったんだけど、そもそもESLintの設定ファイルはこれまで雰囲気で触ってきてたので、旧の設定が新でどうなるかよりも新がどういうものなのか、という観点が僕には良さそう。この記事は新設定目線でまとめられているので、僕にとってちょうどいい。ありがたいー。ざーっと読んで完全に理解した(= 雰囲気だけ理解した)これまでは設定ファイルを独自で解決してたけど、JSの解決の仕組みを使うようにした。ってことかな。それは、分かりやすくていいな。設定を配列で指定しておいてその書き方や項目によっては全体に適用されるものがあるあとで書いたほうはそれまでの設定と合成されたりするってことみたいなので、その観点で公式ドキュメントをチェックすればよさそう。全部をチェックするのは大変だから、自分に関係しそうなところだけピックアップしてみるかな。心のハードルが少し下がったところでFlat Configの仕様を眺めてみる。詳しくは↑を見てねってことで、ざっくり自分が見たことを書くとname がある対象のファイルは files ignores で指定する。指定しなかった場合や、ignores だけを指定した場合の動きは特徴的なので先に把握しておくとよさそうPredefinedの設定はインポートして使うhttps://eslint.org/docs/latest/
1年前
記事のアイキャッチ画像
NeverThrowを触ろうと思ったらESLintのFlat Configを触っていた(前編)
Mitsuyuki.Shiiba
TypeScriptでサーバーサイドのアプリケーションを書いていて、エラーをThrowするより型安全に失敗を返したいなと思って、簡単なResult型をペラっと作って使ったことがある。あまり複雑なことはしたくないからそれで満足しているんだけど、TSKaigiやTSKaigi KansaiでResult型の話をよく耳にしたので、ちゃんとライブラリを一個くらい勉強しようかなと思った。最初にあらすじneverthrowとeslint-plugin-neverthrowを入れてNeverThrowを触ろうとしたら、eslint-plugin-neverthrowがESLintのFlat Configに対応していなかったFlat Configを調べるところから始めて、最終的にeslint-plugin-neverthrowの問題はESLintが提供しているラッパーで解決したやったー!NeverThrowで遊べるぞー!←いまここ(つまりまだ遊んでない)長くなりそうなので途中で切った。NeverThrowを触ってみることにしたResult型が使えるライブラリってどういうのがあるの?って同僚の @kosui_me に聞いたら「fp-ts・Effect・NeverThrowがあるよ」って教えてもらったのと、@sadnessOjisan の記事を見かけてoption-tを知った。fp-ts: https://gcanti.github.io/fp-ts/Effect: https://effect.website/NeverThrow: https://github.com/supermacro/neverthrowoption-t: https://github.com/option-t/option-tほんとにちらっとだけ見てみて、fp-tsとEffectはResult型よりも広い範囲をカバーしてそうだなって感じた。関数型のプログラミングや、実現したいエコシステムがあって、その一つの要素としてResult型があるような印象。それはちょっと僕には大きいので、今回はやめておこうと思った。で、NeverThrowとoption-tはResult型をメインで扱っているのでどちらもいいなーと思いつつ、GitHubを眺めたりしてなんとなくNeverThrowを触ってみることにした。↓ @sadn
1年前
記事のアイキャッチ画像
TSKaigi Kansaiから帰ってきてDuckDB Wasmで遊んだ
Mitsuyuki.Shiiba
カケハシのスタッフとしてTSKaigi Kansaiに参加して楽しんできた。スポンサーのランチLTでカケハシのメンバーも喋るからブースを抜け出して見に行ってたら、newmoの大貫さんのLTで「DuckDB Wasmを使ってクライアントだけで処理したよ!」って発表があって、面白いなーと思ったのだった。kansai.tskaigi.orgので、遊んだDuckDBは初めて知ったし、Wasmには興味があるので、遊んでみた。GitHub Pagesにアップロードしておいた。11/18の日本各地の最高気温が分かるよ!https://bufferings.github.io/vite-react-duckdb-wasm/やってるのはDuckDB Wasmをクライアントで取得してe-Govデータポータルの11/18の最高気温のCSVをDuckDBに入れてそのDBの内容をテーブルに表示テキストボックスの内容が変更されたら、SQLを実行してその値で結果をフィルタリングして表示最高気温のデータは、こちらからダウンロードしておいた。気象観測_最新の気象データ | 最高気温 | e-GovデータポータルCSVで何か適当なデータがほしいなと思ってe-Govからこれを見つけてきたのだ。Shift_JISだったのでUTF-8に変換して、静的コンテンツとしてダウンロードして使っている。面白かった。クライアントで処理するから速いよね。ふむふむ。ソースコードhttps://github.com/bufferings/vite-react-duckdb-wasm/どんなふうに動くのかなーって適当に試しただけなので、本番運用で使えるレベルのものでは全くない。使ってみたものDuckDB Wasmだけ使えたら満足なつもりで始めたのに、せっかく遊ぶんなら!ってちょこちょこ手をだして楽しんでた。Vite (+ React + TS) https://ja.vite.dev/素のHTMLでも楽しめそうだったけど、せっかくならReactとTSを使っておこうと思ってViteで作ったBiome https://biomejs.dev/TSKaigi KansaiでもBiomeの話をよく耳にしたなぁ。と思って、Viteの用意してくれたESLintを全部消して、BiomeでLintとFormatをするようにしてみたWebSto
1年前
記事のアイキャッチ画像
TypeScript 5.8で条件付き戻り値型に対するナローイングができるようになりそう(特定の制約を満たす場合)
Mitsuyuki.Shiiba
数日前にTypeScript 5.7 RCがアナウンスされてリリースが楽しみだなー!ってところなんだけど、そのさらに数日前に、ウォッチしていたこのPR↓がマージされてTypeScript 5.8.0のマイルストーンに入った。わー!これが今日のお話。TypeScript 5.8.0でConditional return type narrowingが入りそう。楽しみ!Conditional return type narrowing?直訳すると「条件付き戻り値型の絞り込み」かな。引数の型によって戻り値の型が変わる関数を定義したいときに、例えばこんな風に書きたくなる。declare const record: Record<string, string[]>;declare const array: string[];function getObject<T extends string | undefined>( group: T): T extends string ? string[] : T extends undefined ? Record<string, string[]> : never { if (group === undefined) { return record; } return array;}const arrayResult = getObject("group");const recordResult = getObject(undefined);この例では、引数である group の型が文字列(またはそのサブタイプ)だったら文字列の配列を、undefined だったら Record<string, string[]> を返すように定義してある。でも、このコードはエラーになる2024-11-07現在のTypeScript 5.6.3では、このコードはエラーになる。呼び出す側のコードはエラーにならずに、渡した引数の型に合わせて正しく戻り値の型が判断されるんだけど(arrayResultの型はstring[]になっている)、関数の実装側の return 部分がエラーになる。「if ブロックの中に入るってことは group が undefined だってことだから戻り値の型は Record<string, string[]> だし、if ブロ
1年前