サクサク読めて、アプリ限定の機能も多数!
トップへ戻る
今年の「#文学」
susisu.hatenablog.com
この記事は はてなエンジニア Advent Calendar 2024 の 4 日目の記事です. 昨日は id:onk さんの コミュニティ生活で大切な三つの袋 - id:onk のはてなブログ でした. ところで皆さん TypeScript は書いていますか書いていますよねそうですよね. そんな皆さんに TypeScript の表現力の高さを改めて実感してもらうべく, この記事では Data types à la carte という論文で紹介されている抽象データ型の拡張性に関する問題を, TypeScript ではどのように解決できるかを紹介します. 抽象データ型の拡張性の問題 まずは論文で紹介されている問題を, (元は Haskell で書かれているので) TypeScript に翻訳しながら見てみましょう. まず A さんが以下のような数式を扱うモジュールを公開したとします. ここで
Kyoto.なんか #6 で発表しました. speakerdeck.com 以下はその補足情報など. 仕様書中の出現頻度 null と undefined がそれぞれの仕様でどの程度使われているのかは, 仕様書中の出現頻度を見るだけでもある程度わかりりそうです. ということで ECMAScript 2024 と WHATWG の標準 (2024-09-07 時点) のうちいくつかの仕様書の中での出現頻度を見てみましょう. Spec #null #undefined ES2024 300 939 DOM 368 120 Fetch 276 10 HTML 1778 275 URL 98 9 見ての通り, 顕著に登場頻度に差があることがわかりますね. なお上記の null の出現数には WebIDL の nullable (T?) を含めていないため, Web 標準における実際の null の
ESLint v9 から Flat Config がデフォルトの設定ファイルの形式となり, 徐々に対応しているプラグインも増えて移行が進みつつありますが, 実際に移行したプロジェクトを見ているとしばしば勘違いなどから誤った設定をしている事例を目にします. ということで, Flat Config を書くにあたっていくつか知っておいて欲しいことや, よく見かけるミスをまとめてみました. この記事では網羅的な説明はしませんので, ESLint や typescript-eslint の公式ドキュメントを前提として, 副読本的に参照してください. Getting Started with ESLint - ESLint - Pluggable JavaScript Linter Getting Started | typescript-eslint Flat Config のしくみ Flat Co
おさらい: prototype JavaScript のオブジェクトはみんな prototype というのを持っていて, この prototype からプロパティを継承, より正確には, プロパティアクセス時にそのプロパティがオブジェクトに存在しなければ prototype を辿って見つけにいくことになっている. あるオブジェクトを prototype とした別のオブジェクトを作るには Object.create を使う (あるいは new 演算子や __proto__ を使っても良い). const x = {}; x.foo = "foo"; const y = Object.create(x); y.bar = "bar"; const z = Object.create(y); z.baz = "baz"; console.log(z.foo); // => "foo" conso
TypeScript でエフェクトを使う話の続き. あるいは DI 手法の話でエフェクトを使うのを半ば冗談として書いていたのを, より具体的な状況を想定してもう少し真面目に考えてみる. ドメイン層と永続化 ドメイン層においては永続化のための具体的な技術については関心を持ちませんが, 永続化すること自体に関心を持たないわけではありません. リポジトリというドメイン層に用意されたインターフェースを使って保存や検索を行いつつ, 具体的な技術はドメイン層の外にあるインターフェースの実装に任せるというのが典型例です. これは永続化以外にも, 例えばイベントの送信などについても同様のことが言えます. (なぜドメイン層では具体的な技術に関心を持たないかというと, ドメインが先で技術が後 (ドメイン駆動) という依存の方向を強制したいからです.) 具体例として, ツイート (†2023) を投稿するメソッ
2 年前に作って放置していたライブラリを最近ちょっと整理したのでその話. エフェクト プログラム中に登場する関数のことを考えてみましょう. 関数は引数を与えるとなんらかの計算を行い, 戻り値を返してくれます. もし関数が純粋な (数学的な意味での) 関数であれば, 関数の入出力は引数と戻り値だけです. つまり, 引数以外の入力 (例えば時刻) によって出力が変わることもなければ, 戻り値以外の出力 (例えば光や音) が得られることもありません. 一方で実用的な価値のあるプログラムというのは時刻に応じて光や音を発生させるようなものであり, これらは純粋な関数だけを組み合わせていては作ることができません. ここで登場する純粋でない関数は, 計算の過程でなんらかのエフェクトを発生させて, 引数と戻り値以外の入出力 (副作用) を行います. エフェクトの明示と副作用の分離 関数が発生させるエフェク
作りました. jsr.io リポジトリはこっち. github.com (2024-05-12 現在, 筆者は Mackerel を開発している株式会社はてなの社員ですが, これは個人プロジェクトで, API ドキュメントなどの公開されている情報に基づいて作成されています.) なぜ JSR になんか publish したくなった 乗るしかないこのビッグウェーブ TypeScript で Mackerel を操作するちょっとしたスクリプトを書きたかったが, 意外とクライアントライブラリがなかった 単に JSON を fetch してくるだけなら簡単だが, それだけでは型もないし使いづらい 使い方 1. JSR からインストール # Deno deno add @susisu/mackerel-client # Node.js npx jsr add @susisu/mackerel-clie
およそ 4 年前に「TypeScript で型レベル Brainfuck」という記事を書きました. susisu.hatenablog.com それから 4 年間の間に TypeScript も進化し, 型レベルプログラミングの技法にも大きな変化がありました. 特に顕著な影響があったものでは, TypeScript 4.0 のタプル型の改善 TypeScript 4.1 のテンプレートリテラル型や条件型での再帰 TypeScript 4.5 の条件型での末尾再帰の除去と, 再帰の上限の緩和 などがあります. こういった変化も踏まえた上で, いまから TypeScript の型レベルプログラミングに入門する人に向けて改めてまとめ直したものがこの記事です. 内容は記事執筆時点の最新版である TypeScript 5.4.5 で動作を確認しています. ぜひ Playground などを使って,
一時間は大体の目安でちゃんと測ってない. PR の作成に時間がかかるときは, 何らか良くないことが起きている可能性が高い 試行錯誤を繰り返している 変更の規模が過大になっている 良くないことが起きているなら, そのまま続けて余計なコストをかけるよりも捨てた方が良い 試行錯誤を繰り返していたなら, そこまでは勉強か練習だと思って捨てる 変更の規模が過大になっていたなら, 分割して作り直すために捨てる 捨てることを躊躇わない 時間をかけるほど引き返しづらくなるので, 一時間くらいで打ち切る 一時間の手戻りなら一時間で取り返せる. なんなら知識が増えている分もっと短くて済む 思ったより進んでいなくても, そのまま思っていたところまで進めようとはせずに諦めて捨てる. とにかく良くないことが起きている可能性が高いので立ち止まるべきで, 思ったところまで進められる (良くない状態に陥らなくなる) 状態
ふと「最寄り」のように「最」と書いて「も」と読ませる語に異常性を感じたので, すべての「最」を集めたくなりました. 昨日のことです. 集めるとはいっても, 脳内を検索しているだけでは, 最上 (もがみ) 最中 (もなか) 最早 (もはや) 最寄り (もより) くらいしか思い当たりません. 直感的に異常性を感じるくらいには珍しいので当然です. ということで衝動買いした広辞苑がこちら. 一般的な語を探すのであればひとまずこれで十分でしょう. このためだけに購入 あとなぜかこれが二つついてきたので, 欲しい方がいたら片方差し上げます. 合計 2,400 万部の実績 そんな広辞苑によれば, 「最」とは次のような接頭語とのこと. も【最】〔接頭〕「まこと」「正しい」「もっとも」の意を表す 「最」に対する理解が深まったところで, 早速この接頭辞が使われる, 我々のまだ見ぬ語を求めて広辞苑を引いてみまし
TypeScript でライブラリ (npm パッケージ) を作るときに, ビルド用の tsconfig を用意することがあります. 例えば以下のような tsconfig.json を作成したとしましょう. { "compilerOptions": { "rootDir": "src", "outDir": "lib", "target": "esnext", "module": "esnext", "moduleResolution": "bundler", "esModuleInterop": true, "strict": true, "sourceMap": true, "declaration": true }, "include": [ "src/**/*" ] } これを使って素朴に tsc -p tsconfig.json のようにビルドすると, src/index.ts
ソフトウェアの品質 ソフトウェアの品質といえば, 大まかに外的品質と内的品質に分けられます. 外的品質: ユーザーから見た品質 例: 安全かつ確実に動作すること, 操作しやすいこと 内的品質: 開発者から見た品質 例: 変更しやすいこと, 型・linter・テストなどに守られていること, 読みやすいこと 当然ですが, ソフトウェア開発者としては外的・内的どちらの品質も高い, という状態を目指すべきでしょう. 場合によって優先度こそあれ, どちらかを一方的に切り捨てるべきではありません. どちらの品質から高めるか ある程度の複雑さを持ったソフトウェア (のコンポーネント) を作る場合, 大抵はどちらの品質も高い状態に向かって一直線に進めることはなくて, まずはどちらか一方の品質が高い状態を目指すのが普通かなと思います. こういった開発の進め方については様々な流派があると思いますが, ここでは
まえがき こんにちは, 人間 ESLint です. そんな人間 ESLint の私ですが, 私一人があらゆるコードに注意深く目を通して, さらに修正案まで提示するというのは大変です. 多くの問題は機械的に検知できるはずなので, そういった仕事を私の代わりにしてくれるメカ人間 ESLint が欲しくなってきます. そんなわけで, 4 年ほど前から ESLint の共有設定を仕込み続けています. この共有設定を作るために, 私自身も ESLint の組み込みのルールや typescript-eslint が提供するルールを何度か全て見直したりしていて, それゆえの人間 ESLint でもあります. github.com Flat Config ところで ESLint の設定ファイルは, 次のメジャーバージョンである v9 以降に新しい形式のもの (通称「Flat Config」) が標準となり
What? Null Object パターンとは プログラムを書いていると, 何もしない・何もないということを表したいシチュエーションがしばしば登場します. ナイーブな実装ではこのような場合に null のような特殊な値を使いますが, 多くの言語では null に対してはメソッドの定義や呼び出しができないため, 以下のようなデメリットがあります1. メソッドを呼び出すたびに都度 null かどうかの検査が必要になり, コードが冗長になる null の場合にどうするかという処理を都度記述することになり, コードの凝集度が下がる null かどうかの検査を忘れてランタイムエラーが発生する Null Object パターンはこれに対する解決案の一つで, 何もしない・何もないことを表すオブジェクト (Null Object) を導入します. これによってメソッド呼び出し前の検査が不要になったり,
TypeScript で DI (依存性注入) するためのライブラリを作ったんですが, それを紹介する前に既存手法をまとめておいた方が説明が楽だなと思ったのでまとめておきます. そもそも DI の目的とは, みたいなところは詳しく説明しないのであしからず. 手法の比較 DI なし Service Locator エフェクト Constructor Injection Setter Injection デコレータ typed-inject 次回予告 手法の比較 DI なし まずは DI を使わない場合を見ていきましょう. ここでは例として, 以下のような時刻と乱数を必要とするコンポーネント MyService が, 時刻と乱数を扱う機能をそれぞれ提供するコンポーネント Clock と Random に依存するような場合を考えます. type Clock = { getTime: () =>
TypeScript で Cake Pattern っぽい DI (依存性注入) をするためのライブラリを作ったので, そのご紹介です. この記事での解説や他の手法との比較は前回の記事を前提とするので, まずはこちらをお読みください. Scala における Cake Pattern Cake Pattern は Scala で DI を実現する方法の一つで, ライブラリやアノテーションを使わず Scala の言語機能のみで完結するシンプルさが特徴です. 以下の例では前回の記事から引き続き, 時刻と乱数を扱うコンポーネントと, それらに依存したコンポーネントがある, というものを使います. まずはコンポーネント ClockComponent, RandomComponent, MyServiceComponent を定義します. Scala の trait は TypeScript でいうと
令和最新版. Vivaldi でアイコンがカスタマイズできるようになっている テーマ設定画面を眺めていたら, Vivaldi 6.0 からツールバーのアイコンがカスタマイズできるようになっているのに気がつきました. 流石 Vivaldi です (?) vivaldi.com しいたけ復活 こうなっては中止ボタンをしいたけにするしかありません. しいたけアイコンは以下で用意されていたものを拝借します*1. バッテン髪飾り派の人も安心. momizine.hatenadiary.org というわけで完成です. フラットデザイン全盛の時代にスキュモーフィックしいたけが悪目立ちしていて最悪ですね. *1:画像の究極の出典ではないかもしれないので, そのあたりの扱いは自己責任で
以前にも同様の記事を書きましたが, 今回はその改訂 + α です. 動作は TypeScript 5.0.4 で確認しています. Playground で試しながら読むとわかりやすいかもしれません. おさらい まずは TypeScript において, 関数に渡される引数に制約をかけたいときに通常使われる方法について思い出しましょう. 要するに引数に対する型注釈 (x: T) のことですね. function myFunction(str: string): void { console.log(str); } myFunction("foo"); // OK myFunction(42); // Error 同じように, 型エイリアスなどの型引数に対しても制約をかけたいこともあります. これは通常は型引数に対して上界 (T extends U) を指定することで実現されます. type M
ちょっと前に話題になった hotscript の技法の紹介やら, ラムダ計算を TypeScript の型にコンパイルする話やらなんやら. 通常の型レベル関数 TypeScript の型エイリアスはパラメータを取れるので, これは型レベルの関数であるとみなせます. type IsNumber<X> = X extends number ? true : false; type A = IsNumber<42>; // = true 一方でこのようにして定義された関数は第一級ではない, つまり関数そのものを他の関数の引数として渡したりすることができません. type FilterUnion<F, X> = X extends unknown ? (F<X> extends true ? X : never) : never; // ~~~~ // ^ Error: Type 'F' is n
たまには雑談っぽいことでも書くか〜 他の開発者のためにコードを書く コードを書くにあたっての話題は尽きることがないです. ざっと思いつくようなことを挙げてみるだけでも, 性質 可読性 柔軟性 堅牢性 変更容易性 原則 驚き最小 DRY SOLID 道具 コーディングスタイル 設計 契約 テスト 型 ドキュメント などなど. ところでこれらが何のために存在するか考えたことがありますか? もちろん最終的にはユーザーにとっての価値を産むということなのですが, コードの書き方自体が直接ユーザーの価値になることはないです. まあほぼ絶対にない. 私は他の開発者のためだと考えています. コードの書き方を工夫することで, 他の開発者が価値を提供するのに役立てば良い. これは単純に私の性質の問題もあって, 元々そういう裏方的なコードを書くのが好きなのもあるし, 必ずしもユーザーに価値を提供することに興味が
TypeScript のオブジェクト型について比較的触れられる機会が少ないこと (重箱の隅) をただ集めただけの記事です. オブジェクト型やその周辺に明るくなりたい人, または任意の重箱の隅が好きな人向け. 挙動の確認は 2022-11-06 時点の最新版である 4.8.4 で行っています. オブジェクト型 プリミティブ値との関係 別の「オブジェクト」型 空のオブジェクト型 interface を使ったオブジェクト型の定義 class を使ったオブジェクト型の定義 オブジェクト型のメンバー プロパティ Index Signatures メソッド Call Signatures Construct Signatures メンバーに対する修飾子 readonly 修飾子 オプショナル修飾子 部分型関係 keyof によるメンバーの列挙 Mapped Types オブジェクト型のキーを使った m
M 年前にも N 年後にも人類は同じ話をしている. まとめ エラーの発生方法は throw と return に大別できる throw には簡潔さ, return には明瞭さと型安全性といった特徴がある どちらの方法がより適しているかはプログラムの規模, エラーの種類, ハンドリングの方法などが判断の材料になる 実際にどちらの方法を使うかは上の判断材料と, フレームワークやプロジェクトのコーディング規約なども合わせて複合的に決めるのがよい エラー発生方法の分類 まず前提として, 関数から呼び出し元にエラーを伝える方法は以下の 2 つに大別できます. 逆にこの記事ではこれ以上の具体的な方法についての議論はしません. throw エラーを throw して呼び出し元に伝える方法です. 例えば以下のようなものが当てはまります. throw new Error("...") Promise.rej
TypeScript 4.8 がリリースされました. これでやることといえば? そう, 数値リテラル型の符号を入れ替えることですね. type Negate<N extends number> = N extends 0 ? 0 : `${N}` extends `-${infer X extends number}` ? X : `-${N}` extends `${infer X extends number}` ? X : number; 動作確認. type Assert<T extends true> = T; type Compat<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false; type A1 = Assert<Compat<Negate<0>, 0>>; type A2 = Assert<C
TypeScript (4.7 時点) において, 文字列に付けられる型には以下の 3 つ (とそのユニオン型) があります. 文字列型 string 文字列リテラル型 ("foo" など) テンプレートリテラル型 (`data-${string}` など) これらのうち, 実行時の入力, 特に事前にパターンが想定されていないような任意の入力が含まれるような文字列に対しては, string や string を含むテンプレートリテラル型を付けることはできても, 文字列リテラル型を付けることはできません. 文字列リテラル型を付けるためには型検査時 (実行の前) に入力文字列の内容がわかっている必要があるので, まあそれはそうですね. このことを利用して, 実行時の入力を含む文字列を与えようとすると型検査に失敗するような関数を作ることができそうです. まずは与えられた型が文字列リテラル型, ま
ここのところ偶然なのか「共通化」という言葉を多く聞いているのですが, その言葉を聞くたびに身構えていることに気がついたので, この気持ちの出どころを共有しておきます. なぜ身構えているかというと, 共通化が必ずしもコードを良い状態にするとは限らないにも関わらず, それ自体が目的になってしまっている (ように見える) ことが多いからです. この手のリファクタリングの目的はあくまでコードの改善のはずで, そのことを忘れて共通化するだけで満足してしまうと, 良くてリファクタリングの効果が半減, 悪ければ逆効果になってしまいます. 個人的にコードを共通化する上で注意してほしいと思っているのは以下の二つです. コードを共通化すべきでない場合もある 共通化されたコードは一般的な原則にしたがって設計されなければならない 似たようなことは歴史の中で何度も繰り返し言われていることだろうと思いますが, 改めて
特に何かしらの出典はありません. プログラムの複雑さに対する大局的で直感的な指標として, 表面積とグラフの構造というのを個人的に意識しているという話. いわゆる code smell をどう嗅ぎつけているか. 表面積 プログラムは最も単純には 1 つの入力チャンネル (引数) と 1 つの出力チャンネル (戻り値) でモデル化できます. 要するに関数ということですが, 関数型プログラミングに限らず大抵は似たような考え方ができます. graph LR yield[ ] -- 引数 --> program[プログラム] -- 戻り値 --> return[ ] 一方で現実世界で価値のあるプログラムとなるためには引数と戻り値だけでは不十分で, 実際にはその他の入出力チャンネルも必要になってきます. 例えば, 可変な変数の読み書き 環境変数の読み取り ユーザー入力の読み取り 画面への出力 ファイル
レビューなり, 過去の経緯を調べる目的なりでコードや Pull Request (以下 PR) を読むとき, 書かれていてほしいと思う内容が書かれていないことが少なくない. 例えば, 背景や目的 全体的な実装方針 (特に複数の PR で一つの目的が達成される場合) これまでやったこと, 今やっていること, この次にやること 実装方法 他の方法との比較検討, あえてやらなかったこと, 今はやらないこと 気持ち 気になっていること, 迷ったこと, 自信がないこと, わかっていないこと ということで, こういうのを列挙してなぜ書いてほしいのかをまとめよう...かと思ったけど, 挙げるときりがないし, それぞれは表層的な問題でしかないのでやめた. では根底に何があるかというと, コードや PR がコミュニケーションとして成り立っているかどうかだと思う. 上に挙げたような個別の要求は, コードや P
TypeScript の型レベルプログラミングのための真面目なスタイルガイドではありません. 型なしラムダ計算で喜ぶような人間が使うための諸刃の剣です. この記事の内容は TypeScript の 2022 年 1 月時点での最新版である 4.5.4 に基づいています. 将来のバージョンでの妥当性は保証しません. 「型〜」「〜型」という用語の「型」はしばしば省略します. 値レベルの話題は一切登場しません. 以前作った型レベル Brainfuck インタプリタはこのスタイルに則っているので参考にどうぞ. いつまでこのネタを引きずるんですか? パラメータに対して制約を付与しない 型定義のパラメータに対して extends を使って制約を付与すると, そのパラメータに与えられる引数を制約を満たすもののみに限定することができます. 例として, 以下の Append には string の部分型のみ
この記事は Mackerel Advent Calendar 2021 の 15 日目の記事です. 昨日は id:kazeburo さんの mkr plugin install 時の403 API rate limit exceededエラーを回避する方法 でした. こんにちは id:susisu です. 普段は Mackerel 開発チームでアプリケーションエンジニアをしています. 先日 mackerelio-labs にて cloudwatch-logs-aggregator という Terraform モジュールをひっそりと公開しました. github.com この記事では, このモジュールについてと, これを使って AWS 環境で動作するアプリケーションのメトリックの監視環境を手早く Mackerel 上に構築する方法を紹介します. アプリケーションのメトリックについて (ここでア
次のページ
このページを最初にブックマークしてみませんか?
『Object.create(null)』の新着エントリーを見る
j次のブックマーク
k前のブックマーク
lあとで読む
eコメント一覧を開く
oページを開く