TsKaigi2024に参加しました!

TsKaigi2024に参加しました

tskaigi.org

2024年5月11日に開催されたTsKaigi2024に参加しました!

TsKaigiとは、TypeScriptをテーマとして様々な方が登壇される技術カンファレンスです。

自分の備忘録のために、聞いたセッションについてのメモを残します

聞いたセッションのメモ・感想ブログ

11:30 ~ 12:00

【TsKaigi】「フロントエンドもバックエンドもインフラも… 全てをTypeScriptで統一したらこうなった」を聞きました - Ohkuraのブログ

13:20 ~ 13:50

【TsKaigi】「TypeScript 関数型バックエンド開発のリアル」を聞きました - Ohkuraのブログ

14:00 ~ 14:30

【TsKaigi】「複雑なビジネスルールに挑む:正確性と効率性を両立するfp-tsのチーム活用術」を聞きました - Ohkuraのブログ

14:40 ~ 15:10

【TsKaigi】「Step by Stepで学ぶ、ADT(代数的データ型)、モナドからEffect-TSまで」を聞きました - Ohkuraのブログ

15:20 ~ 15:50

【TsKaigi】「サービス開発におけるVue3とTypeScriptの親和性について」を聞きました - Ohkuraのブログ

16:00 ~ 16:30

【TsKaigi】「TypeScriptとGraphQLで実現する型安全なAPI実装」を聞きました - Ohkuraのブログ

16:40 ~ 17:10

【TsKaigi】「Track2 16:40 ~ 17:10 のLT」を聞きました - Ohkuraのブログ

17:20 ~ 17:40

【TsKaigi】「Track2 17:20 ~ 17:40 のLT」を聞きました - Ohkuraのブログ

全体を通しての感想

全体を通して、フロントエンドのみならず、tsをバックエンドに採用している話や、tsの言語機能自体の話など、様々なユースケースに対しての話題が多いのがTypeScriptらしさだなと思いました。

中には、裏番組になってしまっていて聞くことはできませんでしたが、TypeScriptでハードウエアを動かしている方もいらっしゃったりなど、私が持っていたTypeScriptができることのイメージがとても広がった日になりました。

また、全体を通して、関数型プログラミングのエッセンスをTypeScriptに導入する話もかなり多い印象でした。積読になっている関数型プログラミングの本を読まないと。。。!

TypeScriptは、これまでは業務で必要な範囲だけ調べるといったような付き合い方をしていたが、今回の様々なセッションを聞く中で、私の知らなかった機能や周辺ツールがたくさんあることを知り、日々の開発のためにも、キャッチアップを加速していこうと感じました!

【TsKaigi】「Track2 17:20 ~ 17:40 のLT」を聞きました

LT

Documentation testsの恩恵(ssssotaさん)

スライド

tskaigi.org

内容メモ

Rustのlinterのソースコード中のドキュメントに関数の入出力が記載されている。

これがDocumentation tests

vite-plugin-doctestなどを用いて、documentation-testを書くことができる

  • 良いてん
    • ソースのすぐそばにテストコードが記載できる
    • ドキュメントに記載したコードを実行して動作が保証できる
    • IDEを介して@動作保証されたサンプルが閲覧できる

ドキュメントにサンプルコードを書いても廃れるし、誰もJSDoc読み書きしていないみたいなことにならないのが良い

しかし、ドキュメンテーションテストは万能ではない。

ライフサイクル関数が必要だったり、mockが必要だったり、UIのテストはかけない。

導入方法は、vite-plugin-doctestをinstallすれば結構すぐに使える

感想

doctestめっちゃいい。。。

今回は、フロントの話だったけど、バックエンドでも書けないか調べたい。

Full TypeScriptだから実現できる世界線(k-ichirofさん)

tskaigi.org

内容メモ

Full TypeScriptっていいの?→コンテキストによる!

どんなところでマッチ?

  • 規模小さめの組織
  • ジュニア多め
  • エンジニアがフルスタック的に動く
  • 個人開発

ではない事例

  • front:ts
  • back: Go
  • infra: Terraform

それぞれ一人みたいな状況。しかし、一人でもかけたら続けられないので危機感があったけど、キャッチアップしながら進められるような余裕もない。

これをfullTsにしたら、結構いい感じに行った。

採用などにも有利に働く

感想

コンテキストによるけど、どんなコンテキストで活きるのかについて知ることができたのがよかった。

確かに個人開発だとしやすそう

Real World Type Puzzle and Code Generation(Yuku Kotaniさん)

スライド

tskaigi.org

内容メモ

型安全 + コードジェネレーター -> Prisma ということで Prisma のコードを読みながら、型がどのようになっているのかを読み解く

$UserPayload型→スキーマから自動生成されている型

GetSelectIncludeResult型関数→ infer して、mappedTypesにして、ゴニョゴニョしている

型パズル 要素分解すると組み立てやすい。

感想

私は今まで基本的な型しかあまり扱って来なかった。ただ、TypeORMの型サジェストとか内部がどうなっているのだろうとか思っていたけど、コードを読み取っていくことで、理解できるものなんだなということを知った。

今回のような複雑な型について、typeormなどの実際のソースを読んでみようと思った。

【TsKaigi】「Track2 16:40 ~ 17:10 のLT」を聞きました

LT

TSのパフォーマンス改善: やじはむ様(@yajihum)

スライド

tskaigi.org

内容について

TSの公式リポジトリにPerfoemanceについての項目がある。

  1. パフォーマンスとな何か、それはなぜ重要なのか
  2. コンパイル改善のための

パフォーマンスとは何か

1.コンパイル速度

ある例だと、50秒かかっていたみたいなことがある → 短縮することで開発生産性に大きく寄与

2.編集体験

tsserverによる型チェックやエラー表示のパフォーマンス

これらのパフォーマンスを改善することで、開発体験や開発効率を上げることができる。

コンパイルしやすいコードを考える必要がある。

コンパイルしやすいコードとは?

typeよりinterfaceを利用する

ポイント

無効な方があった場合にエラーになる

キャッシュされる

型注釈を利用する

型注釈がないと、tscが型のimport先を探しに行ったりなどをしているので、コンパイルが遅くなる。

→ 遅い部分があった時に試してみるみたいな感じでいいらしい。

上記二つはlintで制御できる

複雑な型は名前づけする

関数の戻り値などの型を別で定義しておいたほうが、コンパイルがキャッシュできるので、早くなる。

感想

パフォーマンスといった時に、コンパイル速度などについては時間がかかっても仕方がないものとして諦めていたので、そこの部分を短くできるという考えはあまりなかった。今回を機に考えてみようと思った。

ts-morphを使ってコードリプレイスとASTへのハードルを下げる: 姫野様(@hime)

スライド

tskaigi.org

内容メモ

ASTとは?→抽象構文木というもの。コードを木構造として扱うことができる

どのようなユースケース

例えばbuttonコンポーネントが174個ある

全部のコンポーネントを置き換えたのかわかりにくい。(フロントなので画面を確認すれば一目瞭然だが、コードベースで確認するのは難しい)

ということで、ts-morphと出会った。

感想

なるほど、tsのコードを分析することができるツールなのか。全てを置き換えたかなどについて、自信がなかったりなどの時にとても便利だなと思った。

SWC Transformerから見るTypeScript関数記述ベストプラクティス: hujiyamaorange様 (@fujiyamaorange)

スライド

tskaigi.org

内容メモ

関数記述

関数宣言、関数式、アロー関数がある

これをSWCの観点から、ベストプラクティスを考える。

SWCとはRust製のコンパイラ

変換時の計策結果

関数宣言方式の方が、変換速度が早い。(変換自体にはあまり時間の差は出ない)

emitという処理に差がありそう

関数宣言のみASTの構造がシンプルになっていた

感想

いつも雰囲気で決めていたけど、コンパイル速度などの観点で考えていなかった。気にして書いてみようと思った。

TypeScriptのコード生成を辛くしないために: ノーン様(@nkowne63)

スライド

tskaigi.org

内容について

ts外とのやりとりに対して必要になるコード生成

自分たちで実装しなきゃいけなくなった時に避けるアンチパターンを紹介。

処理だけをコード生成→バグが入っていたり、他のOSSに乗り換えられなくなったりして大変な思いをした。

なので、型を生成して処理は別で書くなどの段階を追うことが良い。

template Literal Typesやmapped Types, Conditional Types

まとめ

コード生成される具体的な処理に依存してしまうと、後々差し替えが効かなくなる

コード生成を行うときは処理だけでなく、型の生成をして、デバッグの難易度を下げたり差し替えをしやすくする。

感想

コード生成を自作することは今までなかったが、もしそのような時には今回聞いたことをちゃんと思い出そうと思った。

【TsKaigi】「TypeScriptとGraphQLで実現する型安全なAPI実装」を聞きました

TypeScriptとGraphQLで実現する型安全なAPI実装

tskaigi.org

Kazuhito Hokamura 様(@hokaccha)

ユビー株式会社 / ソフトウェアエンジニア

スライド

内容メモ

今回の内容はzennにもまとめられている

Kotlin -> node, tsに書き換えなどを行っている

なぜAPIに型をつけたいのか

const response = await fetch("hogehoge")
const user:User = await response.json()

これのuser:Userは祈り。これが通らないと、ぬるぽになる

→ 外部I/Oに対する片付けが不十分だと静的型付けの恩恵を十分に活かせない

ここを起因にして発生するエラーは多い

APIに型をつけるとは?

  1. APIの仕様をスキーマで定義する:そもそもちゃんと界面の型を決めないといけない
  2. スキーマから実装言語の型を生成する
  3. 生成された型を使って実装する

  4. クライアントの実装

  5. サーバーの実装

→ OpenAPI, gRPC, GraphQLなどを用いることで型をつけることができる。

gRPCはツールチェーンで実装まで型をつけてくれる機能がある。GraphQLだと、実装に型をつけるのはオプショナルになっている。

GraphQL Codegen

デファクトになっているコードジェネレーターツール

クライアントの便利機能

Client Presetというクライアント向けのセットがある

重要なポイントとしては、スキーマから型を生成するのではなく、クエリから型を生成する。

GraphQLはクライアントは必要なフィールドを指定できるという機能があるが、スキーマから型を生成すると、すべてのフィールドを含んだ型を生成してしまうので、GraphQLの柔軟性が活かせない

GraphQL CodeGenは、クエリから型を生成することができる!

(だからクライアント側とサーバー側でツールが異なるのか!)

Fragment Colocation

上で取得したデータをコンポーネント内の下層のコンポーネントで情報を使うというような流れ

→ しかし、下層のコンポーネントで、あるフィールドを使わなくなった時に、それに気づけない。どこかで使ってるかもしれないし、安易に消せない。サーバー側から本当は必要ではない情報があるのにいつまでも取得してしまう。

「すべての子が必要とするデータをトップレベルのクエリが知っている必要がある」という設計の歪み

-> Fragment Colocationを用いることで解決できる。

自分に必要な情報だけを定義しておいて、自分と子だけを管理するようにすることで、上のような状況を避けることができる。

→ これをGraphQL CodeGenが対応している!!

Fragment Masking

あるコンポーネントでしか仕様していないフィールドが、他のコンポーネントでも使用しようとしてしまう。大元で情報を取ってきているので、取れてしまうが、Fragment Maskingを用いることによって型エラーを出すことができる。

しかし、型が複雑になるというデメリットもある

サーバーサイド

GraphQL Codegen TypeScript Resolvers

graphql-js, Apollo Server, GraphQL Yogaなどのメジャーなライブラリに対応

NestJSだけちょっと特殊。

サーバーはスキーマから型を生成すれば良い。

例えばスキーマ内の取得しないフィールドがある場合、その取得無駄になることがある。

その時に、フィールドごとにリゾルバを定義することによって、遅延実行をすることができる。

しかし素直にすると、型エラーになってしまうが、打開策もある。

感想

GraphQLを用いて開発していたが、Fragment Colocation(クライアントサイド)や、フィールドごとにリゾルバを設定(サーバーサイド)などについて知らなかった。

今まで課題に感じていた部分の解決になりうる概念だったので、知ることができてとてもよかった。

また、TypeScriptとGraphQLでの web API 開発の親和性の高さを知ることができた。

【TsKaigi】「サービス開発におけるVue3とTypeScriptの親和性について」を聞きました

サービス開発におけるVue3とTypeScriptの親和性について

tskaigi.org

からころ 様(@karan_corons) 株式会社LIXIL Marketing DevOps. System Dev. & Ope. Digital / アプリケーションエキスパート

スライド

内容メモ

はじめに

tsとの親和性とは

型付け × 論理構成

vue.jsの概要

Webユーザーインターフェース構築のための、親しみやすく、パフォーマンスと汎用性の高いフレームワーク

親→子

definePropsで定義した欲しいデータを親コンポーネントから渡してもらうようにする。

子→親

defineEmitsを用いて、排出したいデータを定義しておくと、親がわで受け取ることができる。

これらのことが、型付きで行うことができる。

ツールチェーンについて

Nuxt, Vite, Vitest, Pinia(vue用の状態管理ツール),Volar.js などなど、さまざまなツールチェーンがある。

Vue.jsのtypeScript対応の経緯について

vue2の頃

  • Vueコンポーネントのロジックのみを外部ファイルに分割しずらい
  • 状態管理ツールなどから型が渡ってこない
  • thisやテンプレートに型が反映されない

これらの課題を解決できるような銀の弾丸はなかった。

対応までの流れとして

  1. composition API の登場 Vueコンポーネントを構成するための関数ベースのAPIセットの相性(ref, computed, onMounted, inject, などの実装)
  2. コンポーネントランタイムの型付けが改善する
  3. volar.jsとvuejs/language-toolsの功績

※ composition APIがあるからといって、options apiの開発が非推奨な訳ではない。

具体的にはどうなったの?

  • Composableとしてロジックを外部に切り出すことができるようになった
  • readonlyによって関数によるカプセル化が堅牢になった
  • 状態を複数コンポーネント間で跨ぐこともできる -> しかし、SSR時に不具合を起こしたりするので、piniaを用いることで改善できる

Pinia

Vueから推奨されているtsフレンドリーな状態管理ツール

型を利用してデータフローを可視化しよう

サンプルコード

感想

お仕事とかでvueをたまに書いているけど、当たり前のようにtypescriptで使えるようになっているためにはいろいろな功績があってのことなんだなということを知れた。

result型を用いてエラーハンドリングを行っている。他のセッションでも話されているので一般的な考え方なんだなということを知った。いつもtry-catchでエラーハンドリングしていて辛いと思っていたので調べてみようと思った。

optional APIはてっきり非推奨なものなのだと思っていたがそうでないことを知れてよかった

【TsKaigi】「Step by Stepで学ぶ、ADT(代数的データ型)、モナドからEffect-TSまで」を聞きました

Step by Stepで学ぶ、ADT(代数的データ型)、モナドからEffect-TSまで

tskaigi.org

竹下義晃 様(@takezoux2)

レバレジーズ株式会社 / テクノロジー戦略室 室長

内容メモ

関数型と聞いてどんな印象がありますか?

  • すでに身近で使っている
    • Arrayのfilter, map, flatMap, reduceは関数型から取り入れられたもの
  • 関数型の概念も、根底には何かしらの課題があって、それを解決している

今回の説明範囲

  • ADT, Either, TaskEither, Effect-tsについて

サンプルコード

emailとpasswordでユーザー登録をおこない、完了メールを送る

普通に書くなら

function createUser(param) {
  if( !validateInput(param)) {
    throw
  }
  try {
    result = save(param)
  } catch {
    throw
  }
  sendRegisteredEmail(param.email)
}

→ 失敗成功のT/Fをどうするかが人による → 地味にif多い

これらをADTを取り入れていい感じにする

ADTを導入する

ADTとは?

直積型と直和型を組み合わせて表現される型

直積:{name: string, age: number} -> nameとageの直積

直和:"google" | "FaceBook" | "X"

validateInput

戻り値を Success| {type: "InvalidEmail", message: ""}

を返すようにする

→ 型定義だけでメソッドの挙動がわかるようになった

(感想メモ:失敗の理由が返ってくるの。シンプルな解決だけど強力だなぁ)

しかし、まだifは減っていない

Either

を導入する

Eitherとは、Either<Left, Right>の型定義を持つMethod

Rightに正しい状態を返す。

成功と失敗を型で表現することができるようになった書き方

usecaseの方では、isLeft(), isRight()の方で成功か失敗を判別すればいい

→ まだif文多い。ちょっと冗長になっている

pipeを導入する

これを導入することによって、if文がなくなった。

処理を分割して、組み合わせやすくなった。

しかし問題点として、型パズルやPromiseどうするの?という話がまだある。

PromiseとEitherが相性悪い

普通に書こうと思うと、コンパイルエラーになってしまう。そのため、TaskとTaskEitherを用いる。

Task<T>は処理を型で表現したMonad

実態は() => Promise<T>

これをすることで、PromiseもMonadに巻き込むことができた。しかし、型パズル感が増えた

Effect-TSを導入する

Effect Monadを中心としたライブラリ

Effect<Success, Error, Requirements>の型を持つ

Either, TaskEitherをEffectで置き換える

これをすると、Effectがすべての型を含んでいるので、型パズル間を減らすことができる。

generatorスタイルも使える→switchやif文を使った方がわかりやすいケースもEffectで対応することができる

効果

  • バグが減る
    • ロジックまで型で表現できて、コンパイラがチェックしてくれる
  • 拡張性、メンテナンス性UP
    • 処理をより小さく分割しやすい

感想

今回のStep by stepの流れの中で、関数型言語の良い部分について知ることができた。

自分が不勉強で、monadなどについてちゃんと理解できなかったので勉強しよう!

if文を減らすことができるのが感動的。試してみたくなった。

【TsKaigi】「複雑なビジネスルールに挑む:正確性と効率性を両立するfp-tsのチーム活用術」を聞きました

複雑なビジネスルールに挑む:正確性と効率性を両立するfp-tsのチーム活用術

tskaigi.org

kosui さん

株式会社カケハシ / テックリード

@kosui_me

内容メモ

発表の狙い

複雑なビジネスロジックでも、正確性と効率性を両立するために、どのようにfp-tsを活用したか、チームで運用するためにどんな工夫をしたのか。

SaaSエンタープライズ対応と、Excel一括入稿の重要性の背景

今までDXが進んでいなかった分野でも、デジタル化が進んでいる。

大企業や行政でもバーティカルSaaS業界も採用されてきて、要求される機能や品質も変化していった。

個人、抽象企業向けだと、データは少量で構造もシンプル。なのでGUIからデータ入力できればよかった。

しかし、大企業や行政向けだと、GUIから入力するのが現実的ではなくなる

これをExcel一括入稿したいと考える。

しかし、これが辛い

なぜ辛いか

入稿してみる→エラーが出てくる→修正して入稿する→エラーが出てくる

の繰り返しになってしまう。

しかもエラーが「不正なデータです」になりがち

表形式データの検証にて、正確、効率的にエラーをフィードバックするか

表形式データ検証の3ステップ

  • セルをパース:不正な日付、絵文字の混入
  • 行を検証:行の重複関係を検証(IDの重複チェック
  • 表を検証:他のテーブルとの依存関係をチェック(外部キーなどにならないといけないものが存在するかのチェックなど)

理想的には、それぞれのステップすべてを並行で行なって、エクセルファイルの中のすべての不正な部分を出すようにしたいが、一般的には一つのステップずつやるので、何度も顧客に入れてもらわなければならなくなる。

→ そこでfp-tsを用いる

fp-tsによるエラー合成

TypeScriptのエラー処理

  • ユーザー定義エラーを投げる(エラーオブジェクトを拡張して作る)
  • Either型を返す

ユーザー定義エラーを投げる場合

複数のエラーを同時に伝播しずらい。

「IDも名前も誕生日もすべてダメだった」というときに面倒。try-catchでarrayに詰め込んで...ってやればできなくもないが面倒

Either型(Result型)を用いる場合

Either型は判別可能なユニオン型

しかし、これを導入しても、欲しい情報を取得するのが大変でifが大量に出てきてしまう。

→ 外部ライブラリに頼りたい → fp-tsを用いる!

実際に合成すると

表形式のエラーもfp-tsを用いることによって、すべての行についてバリデーションを一括で行い、それのエラーハンドリングがやりやすくなる。

表形式データの依存関係の解決

表形式データ内にあるデータ不整合も型で防ぐことができる。

newtype-ts(公称型)を用いることで検査可能になる。

「検査を通ったものですよ」ということを表す型?で、検査前のデータを入れるとエラーになるようなものを導入することで問題をクリアできる。

難しさ

あまり事例がない。関数型プログラミングの知見が一般的ではないので、チームで運用するのに難しさを感じた。

そこでオンボーディングコストを減らすような体制を整備した。

ポイントとして

  • スコープを制限する
  • 短期集中でペアプロ・モブプロ
  • 社内向けレシピ集とプレイグラウンド

(WebContainerというのを初めて聞いた)

質問

  • fp-tsはバンドルサイズが大きすぎたりしないか
    • フロントなどで利用しているものではないのと、あまり頻繁に行われる機能でもないので、パフォーマンスについてはスコープアウトしている
  • スイッチングコスト
    • それぞれの機能ごとにベストを探っている。社内標準のようなものがあるわけではない
  • fp-ts以外のライブラリで検討したものがあったか

感想

Excel入稿機能に携わった経験はあるが、確かに「一つエラーを見つけ次第エラーを返す」みたいな実装になっていた。顧客の便利さを考えた時には、シートのどこにどんなエラーがあったかの情報を全て返してあげることで、顧客の利便性が明らかに向上すると実感することができた。

また、その解決に対して、関数型プログラミングのノウハウが活きるのは目から鱗だった。