第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' ]
このように、より詳細なバリデーションルールを設定できることが分かります。
