第2章 TypeScriptの開発を加速するエコシステム
入力値の検証をサポートする「zod」
今回は、@typesで型定義ファイルが配布されていることを確認しました。このようにTypeScriptは単体で機能しているのではなく、多くの開発者が提供するエコシステム(周辺ツールやライブラリ群)によって機能しています。
そうした中から、1つの例として、有名なものを紹介します。バリデーションライブラリ「zod」です。バリデーションは確認や検証を意味する言葉です。
それでは、またプロジェクトを作りましょう。ファイル構成とpackage.json
、tsconfig.json
の設定は先ほどと同じです。ファイル構成だけを示します。
▶ファイル構成
-
プロジェクトのルート/
- dist/
-
src/
- index.ts
- package.json
- tsconfig.json
zodをインストールします。
npm install zod
zodの使用例1:parse
による検証
src/index.js
に、データを確認する簡単なコードを書きます。
まずは、型を定義します。
import { z } from 'zod'; const UserSchema = z.object({ name: z.string(), age: z.number(), }); type User = z.infer<typeof UserSchema>; // 型推論でUser型を作成
zodからz
をインポートします。そしてz
を利用してUserSchema
を作り、z.infer<typeof UserSchema>
でUser型を作ります。
次に、データを取得・検証するfetchUser
関数を作成します。
async function fetchUser(id: number): Promise<User> { const fakeFetch = [ {"name": "Tom", "age": 10}, {"name": "Bob"}, ]; const data = fakeFetch[id]; return UserSchema.parse(data); // 検証+型変換 }
本来ならawait fetch(~)
とデータを取得するところですが、fakeFetch
というオブジェクトの値で代用します。
UserSchema.parse
で、検証と型変換をおこないます。成功したときはUser型のオブジェクトが返ります。失敗したときは例外が発生します。
最後に、fetchUser
関数を利用した処理を記述します。
for (let i = 0; i < 3; i ++) { try { let res: User = await fetchUser(i); // 失敗時は例外発生 console.log(i, res); } catch(e) { if (e instanceof z.ZodError) { const msg = e.issues.map(x => x.message); console.log(i, "失敗", msg); } } }
User
型のオブジェクトres
を得て出力します。失敗したときは例外が発生します。catch
で捕捉するe
がz.ZodError
のインスタンスならメッセージを得て出力します。
実行結果を示します。
0 { name: 'Tom', age: 10 } 1 失敗 [ 'Required' ] 2 失敗 [ 'Required' ]
fakeFetch[id]
の値が正しいUser型なら、取得したデータが表示されます。欠けたデータや、undefined
のときは失敗します。
zodの使用例2:詳細なルールとsafeParse
関数の活用
src/index.js
のプログラムを書き換えます。
まずは前半です。
import { z } from 'zod'; const UserSchema2 = z.object({ name: z.string() .min(1, "1文字未満") .max(8, "8文字超過") .regex(/\D/), email: z.string() .email(), age: z.number() .int() .positive() .optional(), }); type User2 = z.infer<typeof UserSchema2>;
zodでは、各プロパティに対して検証方法を細かく指定できます。また失敗時のメッセージを設定することも可能です。
zodにはさまざまな検証方法が用意されています。次の公式ページにまとまっています。
次は後半です。
const data = [ {name: "Tom", email: "x@example.com", age: 10}, {name: "Bob", email: "x@example.com", age: 11}, {name: "Sam", email: "x[@]example.com"}, {name: "HogeHoger", email: "x@example.com"}, {name: "Tom3", email: "x@example.com"}, {name: "Ham", email: "x@example.com", age: 0.3}, ] data.forEach(x => { const res = UserSchema2.safeParse(x); if (res.success) { console.log('成功:', res.data); } else { const msg = res.error.issues.map(x => x.message); console.log('失敗:', msg); } });
検証用のデータを用意して、1つずつ検証しています。
最初の例ではparse
関数を使い、検証に失敗した際は例外を発生させました。ここではsafeParse
関数を使います。
safeParse
関数を使えば、失敗しても例外は発生しません。res.success
で成功したかを判定して、処理を分岐できます。
実行結果を示します。
成功: { name: 'Tom', email: 'x@example.com', age: 10 } 成功: { name: 'Bob', email: 'x@example.com', age: 11 } 失敗: [ 'Invalid email' ] 失敗: [ '8文字超過' ] 成功: { name: 'Tom3', email: 'x@example.com' } 失敗: [ 'Expected integer, received float' ]
このように、より詳細なバリデーションルールを設定できることが分かります。