0Pricing
TypeScript Academy · レッスン

スキーマから型へのマッピング

テーブル定義がTypeScriptの型になる仕組みを学びます。

「スキーマから型へのマッピング」はCoddyKit上の無料TypeScript Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはTypeScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 TypeScript Academyコースには全4レッスンが含まれています。

テーブル定義から行型へ

型安全なORMを使うと、データベーステーブルを1度だけ記述し、そこからTypeScriptの行型を自動的に導出できます。行の形状を手作業で記述する必要はありません。カラム定義から計算されます。

このレッスンでは、実例としてDrizzleスタイルのpgTableを使用します。

テーブルの定義

テーブルは、カラム名とカラムビルダーを対応付けるオブジェクトです。各ビルダーには、SQL型とnotNullやprimaryKeyなどの修飾子が組み込まれています。

import { pgTable, serial, text, integer, boolean } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: serial("id").primaryKey(),
  name: text("name").notNull(),
  age: integer("age"),
  isAdmin: boolean("is_admin").notNull(),
});

カラム型の推論

各カラムビルダーは、生成する値を表すファントム型を持ちます。serialとintegerはnumberに、textはstringに、booleanはbooleanに対応します。

ビルダーは、カラムがnullを許容するかどうかも追跡します。

import { InferSelectModel } from "drizzle-orm";

// Row type inferred from the table above
type User = InferSelectModel<typeof users>;
// {
//   id: number;
//   name: string;
//   age: number | null;   // not notNull -> nullable
//   isAdmin: boolean;
// }

notNull が null許容性を制御

行の型を決めるうえで、最も重要な要素は notNull() です。これを付けないカラムは、推論される型では T | null になります。SQLではデフォルトでNULLが許容されるためです。

const posts = pgTable("posts", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),  // string
  subtitle: text("subtitle"),      // string | null
});

type Post = InferSelectModel<typeof posts>;
// { id: number; title: string; subtitle: string | null }

SELECTモデルとINSERTモデル

読み取り(select)用の行型は、書き込み(insert)用の型とは異なります。insertでは、デフォルト値が設定されたカラムや自動インクリメントされるカラムは省略可能になります。

import { InferInsertModel } from "drizzle-orm";

type NewUser = InferInsertModel<typeof users>;
// {
//   id?: number;        // serial has a default -> optional
//   name: string;
//   age?: number | null;
//   isAdmin: boolean;
// }

型推論の仕組み

内部では、各カラムビルダーは PgColumn<{ data: number; notNull: true }> のようなジェネリッククラスです。マップ型がテーブルのすべてのキーを走査し、それらのフラグを読み取ります。

// Simplified mental model of the inference
type InferRow<T> = {
  [K in keyof T]: T[K] extends { _: { data: infer D; notNull: infer N } }
    ? N extends true ? D : D | null
    : never;
};

列挙型とカスタム型

文字列ユニオン型のカラムは、単なる string ではなくリテラルユニオン型に対応付けられます。これにより、無効なステータス値はコンパイルエラーになります。

import { pgEnum } from "drizzle-orm/pg-core";

export const roleEnum = pgEnum("role", ["user", "admin", "owner"]);

export const members = pgTable("members", {
  id: serial("id").primaryKey(),
  role: roleEnum("role").notNull(), // "user" | "admin" | "owner"
});

デフォルト値

.default() を使うと、カラムはinsert時には省略可能になりますが、select時には存在したままになります。型システムは「デフォルト値あり」を記録し、適切な箇所だけで省略可能性を切り替えます。

const events = pgTable("events", {
  id: serial("id").primaryKey(),
  createdAt: text("created_at").notNull().default("now()"),
});
// Select: createdAt is string
// Insert: createdAt is optional (default fills it)

なぜ重要なのか

行型は導出されるため、スキーマを変更すると、すべてのクエリ結果の型が自動的に更新されます。カラム名を変更すれば、古い参照をコンパイラーがすべて指摘します。不整合が生じる別の情報源はありません。

小さなエンドツーエンド例

ここまでの内容を組み合わせると、定義を一度だけ行い、型を推論し、それをリポジトリ関数の契約として利用できます。

type User = InferSelectModel<typeof users>;

async function findUser(id: number): Promise<User | undefined> {
  // db.query returns rows already typed as User
  const rows = await db.select().from(users).where(eq(users.id, id));
  return rows[0];
}

落とし穴: notNull の付け忘れ

本来必須のカラムに notNull() を付け忘れると、推論される型が T | null になり、コード全体で不要なnullチェックが必要になります。スキーマは実態に忠実に保ってください。

理解度チェック

スキーマから型への対応付けについて、理解度を確認しましょう。

まとめ

テーブル定義が唯一の信頼できる情報源になることを学びました。カラムビルダーはファントム型を保持し、マップ型がテーブルを走査して行型を構築します。notNull() がnull許容性を制御し、selectモデルとinsertモデルでは省略可能性が異なります。スキーマを変更すれば、すべての結果の型も自動的に追従します。

よくある質問

「スキーマから型へのマッピング」レッスンは無料ですか?

はい。「スキーマから型へのマッピング」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、TypeScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 TypeScript Academyコースには全4レッスンが含まれています。

「スキーマから型へのマッピング」で何を学びますか?

テーブル定義がTypeScriptの型になる仕組みを学びます。 ブラウザで直接実行するハンズオンコードでTypeScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

TypeScript Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのTypeScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「スキーマから型へのマッピング」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このTypeScript Academyレッスンでコードを書いて実行できますか?

はい。すべてのTypeScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. スキーマから型へのマッピング
  2. 型安全なクエリ構築
  3. クエリ結果の形状推論
  4. 型によるリレーションとJOIN
← TypeScript Academyに戻る