Skip to content

バリデーションとTypeScript の見かけ上の型

(注意: このページは中心部分は完成してるけど、 Wrapper (導入とか) がまだ不足してるよ!)

TypeScript ではなぜ不十分か

TypeScript は、一見完全に型がついてるように見えるよね。

index.ts
const num: number = 10 as any; // "10" as any に置き換えてみて
num.toFixed(2); // -> '10.00'

でも、この型はコンパイル時のみに存在するもので、実行時には完全に消えてるよ。

Terminal window
bunx tsc ./index.ts --outFile ./out.js
out.js
const num = 10;
num.toFixed(2);

これは、 Bun や Deno などの TypeScript を直接実行できると謳っているランタイムでも同じ。

バリデーションをしないと、例えばパスワード認証スキップとかの荒業ができるよ。 (複雑なので詳細は発展演習に回すね)

なので、バリデーションは必要!

(NOTE from devs: 導入は適当だけど、必要なのは正しいよ。)

バリデーションをしてみる

では、実際に TypeScript でスキーマを作り、既存のアプリケーションにバリデーションを追加してみよう。

もととなるアプリケーションはこれ。 ソースコードが欲しい場合は、このドキュメントのリポジトリをクローンして、アプリのあるディレクトリ exercises/t1-validation.install/ を開いてね。

const app = new Hono();
app.patch("/users/:id", async (c) => {
const id = Number.parseInt(c.req.param().id);
type Body = {
password: string;
data: { name?: string; password?: string };
}
const body = (await c.req.json()) as Body;
const res = await prisma.user.update({
where: { id, password: body.password },
data: body.data,
});
return c.json(res, 201);
});
import * as v from "valibot";
const RequestSchema = v.object({
password: v.string(),
data: v.object({
name: v.optional(v.string()),
password: v.optional(v.string()),
}),
});
app.patch("/users/:id", async (c) => {
const id = v.parse(v.number(), Number.parseInt(c.req.param().id));
const body = v.parse(RequestSchema, await c.req.json());
const res = await prisma.user.update({
where: { id, password: body.password },
data: body.data,
});
return c.json(res, 201);
});

(以下蛇足) Hono のヘルパー関数

ちなみに、Hono だともっと便利なバリデーターが提供されてるよ。

import { vValidator } from "@hono/valibot-validator";
app.patch(
"/users/:id",
vValidator(
"param",
v.object({ id: v.pipe(v.string(), v.transform(Number.parseInt)) }),
),
vValidator(
"json",
v.object({
password: v.string(),
data: v.object({
name: v.optional(v.string()),
password: v.optional(v.string()),
}),
}),
),
async (c) => {
const { id } = c.req.valid("param");
const body = c.req.valid("json");
const res = await prisma.user.update({
where: { id, password: body.password },
data: body.data,
});
return c.json(res, 201);
},
);

演習問題 (発展)

バリデーションのない脆弱なアプリケーション exercises/t1-validation.hack-this をハックしてみよう。

要件は次の2つ。

  • server/ の中は変更しない
  • bun server で起動したアプリケーションに対して、パスワードを知らずに、ユーザーの名前を勝手に変える

詳しくはそのディレクトリにある README を見てね。