Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Powerfully Typed TypeScript
Search
euxn23
May 11, 2024
Programming
4
3.1k
Powerfully Typed TypeScript
TSKaigi 2024
https://tskaigi.org/
Lightning Talk
euxn23
May 11, 2024
Tweet
Share
More Decks by euxn23
See All by euxn23
型付き API リクエストを実現するいくつかの手法とその選択 / Typed API Request
euxn23
10
7.1k
TypeSpec を使い倒してる #kyotojs 22 / Use up TypeSpec
euxn23
2
1.1k
NestJS アプリケーションから Swagger を自動生成する / nestjs-meetup-tokyo-01
euxn23
1
2.3k
Other Decks in Programming
See All in Programming
Django Ninja による API 開発効率化とリプレースの実践
kashewnuts
0
980
Reduxモダナイズ 〜コードのモダン化を通して、将来のライブラリ移行に備える〜
pvcresin
2
690
Model Pollution
hschwentner
1
180
Advance Your Career with Open Source
ivargrimstad
0
350
実践AIチャットボットUI実装入門
syumai
7
2.5k
CSC509 Lecture 04
javiergs
PRO
0
300
Back to the Future: Let me tell you about the ACP protocol
terhechte
0
130
CSC509 Lecture 01
javiergs
PRO
1
430
Web技術を最大限活用してRAW画像を現像する / Developing RAW Images on the Web
ssssota
2
1.2k
あなたの知らない「動画広告」の世界 - iOSDC Japan 2025
ukitaka
0
400
私達はmodernize packageに夢を見るか feat. go/analysis, go/ast / Go Conference 2025
kaorumuta
2
490
どの様にAIエージェントと 協業すべきだったのか?
takefumiyoshii
2
610
Featured
See All Featured
The Language of Interfaces
destraynor
162
25k
Stop Working from a Prison Cell
hatefulcrawdad
271
21k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
9
580
Building an army of robots
kneath
306
46k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
61k
The Cult of Friendly URLs
andyhume
79
6.6k
Reflections from 52 weeks, 52 projects
jeffersonlam
352
21k
The World Runs on Bad Software
bkeepers
PRO
71
11k
Large-scale JavaScript Application Architecture
addyosmani
514
110k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
53k
A better future with KSS
kneath
239
17k
Building Adaptive Systems
keathley
43
2.8k
Transcript
Powerfully Typed TypeScript
fp-ts ほどガチガチじゃなくて 部分的に使えるライブラリを 5 分で紹介
zod 言わずと知れたバリデーション用ライブラリ 構造の定義からランタイムバリデータと型定義を生成できる infer が TS Server への負荷が高いので多用に注意 https://github.com/colinhacks/zod#basic-usage import
{ z } from "zod"; const User = z.object({ username: z.string(), }); User.parse({ username: "Ludwig" }); type User = z.infer<typeof User>;
neverthrow TS に Result 型をもたらしてくれるライブラリ throw の代わりに使ってエラー時の処理を追いやすくする 0 Dependencies https://zenn.dev/euxn23/articles/505fd9297eb2dc
const fetchUser = async (): Promise<Result<User, Error>> => { const response = await fetch(...) if (response.ok) { return ok((await response.json()) as User) } else { return err(new Error(...)) } } const fetchResult = await fetchUser() if (fetchResult.isErr()) { return fetchResult.error // <- Error 型である } return fetchResult.value // <- User 型である
ts-pattern TS にパターンマッチをもたらしてくれるライブラリ 0 Dependencies import { match, P }
from 'ts-pattern'; type Data = | { type: 'text'; content: string } | { type: 'img'; src: string }; type Result = | { type: 'ok'; data: Data } | { type: 'error'; error: Error }; const result: Result = ...; const html = match(result) .with({ type: 'error' }, () => <p>Oups! An error occured /p>) .with({ type: 'ok', data: { type: 'text' } }, (res) => <p>{res.data.content}</p>) .with({ type: 'ok', data: { type: 'img', src: P.select() } }, (src) => <img src={src} >) .exhaustive();
TypeScript だけじゃない……! OpenAPI + TypeScript で さらに Powerful に
openapi-typescript & openapi-fetch openapi-typescript は OpenAPI Schema から TS コードを生成
openapi-fetch はそれを利用した API Client コードを提供 https://github.com/drwpow/openapi-typescript/tree/main/packages/openapi-typescript#readme $ npx openapi-typescript ./path/to/my/schema.yaml -o ./path/to/my/schema.d.ts import { paths, components } from "./path/to/my/schema"; // <- generated by openapi-typescript // Schema Obj type MyType = components["schemas"]["MyType"]; // Path params type EndpointParams = paths["/my/endpoint"]["parameters"]; // Response obj type SuccessResponse = paths["/my/endpoint"]["get"]["responses"][200]["content"]["application/json"]["schema"]; type ErrorResponse = paths["/my/endpoint"]["get"]["responses"][500]["content"]["application/json"]["schema"];
openapi-typescript & openapi-fetch https://github.com/drwpow/openapi-typescript/tree/main/packages/openapi-fetch#readme import createClient from "openapi-fetch"; import type
{ paths } from "./my-openapi-3-schema"; // generated by openapi-typescript const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" }); const { data, // only present if 2XX response error, // only present if 4XX or 5XX response } = await client.GET("/blogposts/{post_id}", { params: { path: { post_id: "123" }, }, }); await client.PUT("/blogposts", { body: { title: "My New Post", }, });
orval こちらも OpenAPI Schema から API Client を生成するライブラリ Axios を標準として
Custom Client を生成できる口がある。TanStack Query や SWR、 Zod との 連携もある。 https://github.com/anymaniax/orval/blob/master/samples/react-app import type { CreatePetsBody } from '../model'; import { customInstance } from '../mutator/custom-instance'; export const createPets = ( createPetsBody: CreatePetsBody, version: number = 1, ) => { return customInstance<void>({ url: `/v${version}/pets`, method: 'POST', headers: { 'Content-Type': 'application/json' }, data: createPetsBody, }); };
openapi-fetch と orval はおなじくらい
OpenAPI の定義も Powerful に
typespec TypeScript / C# 風味の DSL で OpenAPI Schema を書けるライブラリ
OpenAPI の yaml を手で書くのは大変だしエディタサポートが貧弱 TypeSpec は VSCode 向けの LSP を提供している 複雑でない module システムによりファイルの分割が容易に可能 コンパイル時に valid な記法か確認されるので安心
typespec https://typespec.io/ import "@typespec/http"; using TypeSpec.Http; model Store { name:
string; address: Address; } model Address { street: string; city: string; } @route("/stores") interface Stores { list(@query filter: string): Store[]; read(@path id: Store): Store; }
@hono/zod-openapi Hono の endpoint とparam の定義に zod を使うと OpenAPI も生えてくる
https://github.com/honojs/middleware/tree/main/packages/zod-openapi import { z } from '@hono/zod-openapi' const ParamsSchema = z.object({ id: z .string() .min(3) .openapi({ param: { name: 'id', in: 'path', }, example: '1212121', }), })
@hono/zod-openapi UserSchema の定義 const UserSchema = z .object({ id: z.string().openapi({
example: '123', }), name: z.string().openapi({ example: 'John Doe', }), age: z.number().openapi({ example: 42, }), }) .openapi('User')
@hono/zod-openapi route の定義 import { createRoute } from '@hono/zod-openapi' const
route = createRoute({ method: 'get', path: '/users/{id}', request: { params: ParamsSchema, }, responses: { 200: { content: { 'application/json': { schema: UserSchema, }, }, description: 'Retrieve the user', }, }, })
@hono/zod-openapi app をセットアップすれば完成 import { OpenAPIHono } from '@hono/zod-openapi' const
app = new OpenAPIHono() app.openapi(route, (c) => { const { id } = c.req.valid('param') return c.json({ id, age: 20, name: 'Ultra-man', }) }) // The OpenAPI documentation will be available at /doc app.doc('/doc', { openapi: '3.0.0', info: { version: '1.0.0', title: 'My API',
@hono/zod-openapi TypeScript の型情報の共有のみに頼り切らず OpenAPI エコシステムを利用 Framework Agnostic に成熟している path や
param 、 status code など詳細な定義が可能 最悪 Hono をやめても OpenAPI が残る 実装と API 定義が一体化しているため、 OpenAPI Schema が嘘になりにくい 実装から OpenAPI Schema を吐くことの良し悪しはケースバイケースで検討が必要
と書いていたところ、なんと Hono 公式で RPC が出た https://hono.dev/guides/rpc#client const route = app.post(
'/posts', zValidator( 'form', z.object({ title: z.string(), body: z.string(), }) ), (c) => { // ... return c.json( { ok: true, message: 'Created!', }, 201 ) } ) export type AppType = typeof route
Hono RPC client 側のコードは以下 import { AppType } from '.'
import { hc } from 'hono/client' const client = hc<AppType>('http://localhost:8787/') const res = await client.posts.$post({ form: { title: 'Hello', body: 'Hono is a cool project', }, }) if (res.ok) { const data = await res.json() console.log(data.message) }
Hono RPC OpenAPI を介さず Hono のみで完結する仕組み。実体は fetch と REST API
である zod 以外でも validator で params の型が定義されていれば OK 前述の @hono/zod-openapi とも組み合わせられるので、 OpenAPI の出力も可能 OpenAPI Client の生成を介さないので非常にパワフル だが、実は OpenAPI エコシステムを介してもあまり負担ではない プロジェクトが大きくなったときに AppType の推論がどれくらい重くなるかは未知数 OpenAPI Schema を介した自動生成では TS 側の負荷をオフロードできるメリットは大きい
まとめ 部分的に入れられるライブラリで小さくはじめよう TypeScript だけでなく OpenAPI エコシステムも強力 Hono の OpenAPI や
RPC はフル TS で開発する上でパワフル
自己紹介 ユーン (@euxn23) from ドワンゴ教育事業 フロントだけじゃない RABBIT 小隊が好き