0Pricing
TypeScript Academy · บทเรียน

การแมปจากสคีมาเป็นชนิดข้อมูล

ทำความเข้าใจว่านิยามตารางกลายเป็นชนิดข้อมูล TypeScript ได้อย่างไร

การแมปจากสคีมาเป็นชนิดข้อมูล เป็นบทเรียน TypeScript Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน TypeScript Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

จากนิยามตารางสู่ประเภทแถว

ระบบ ORM ที่ปลอดภัยด้านประเภทช่วยให้คุณอธิบายตารางฐานข้อมูลเพียงครั้งเดียวและอนุมานประเภทแถวของ TypeScript ได้โดยอัตโนมัติ คุณไม่ต้องเขียนโครงสร้างแถวด้วยตนเอง เพราะระบบคำนวณจากนิยามคอลัมน์

ในบทเรียนนี้ เราใช้ pgTable ในรูปแบบ Drizzle เป็นตัวอย่างต่อเนื่อง

การกำหนดตาราง

ตารางคือออบเจ็กต์ที่จับคู่ชื่อคอลัมน์กับตัวสร้างคอลัมน์ ตัวสร้างแต่ละตัวจะเข้ารหัสประเภท 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

ตัวสร้างยังติดตามด้วยว่าคอลัมน์นั้นสามารถเป็นค่าว่างได้หรือไม่

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 ควบคุมความเป็นค่าว่าง

สิ่งที่มีผลสำคัญที่สุดต่อชนิดแถวคือ notNull() คอลัมน์ที่ไม่มี 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;
};

ชนิด enum และชนิดกำหนดเอง

คอลัมน์แบบยูเนียนของสตริงจะถูกแมปเป็นยูเนียนของค่าลิเทอรัล ไม่ใช่เพียง 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 และบังคับให้ตรวจสอบค่าว่างโดยไม่จำเป็นทั่วทั้งโค้ด ควรรักษาสคีมาให้ตรงกับความเป็นจริง

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจเรื่องการแมปจากสคีมาเป็นชนิดข้อมูล

สรุป

คุณได้เรียนรู้ว่าคำจำกัดความของตารางเป็น แหล่งความจริงเพียงแหล่งเดียว ตัวสร้างคอลัมน์เก็บชนิดข้อมูลแฝงไว้ ชนิดแบบแมปจะไล่ดูตารางเพื่อสร้างชนิดแถว notNull() ควบคุมความเป็นค่าว่าง และชนิดสำหรับ select กับ insert มีความแตกต่างกันในเรื่องการเป็นตัวเลือก เมื่อเปลี่ยนสคีมา ชนิดผลลัพธ์ทุกแห่งจะเปลี่ยนตามโดยอัตโนมัติ

คำถามที่พบบ่อย

บทเรียน “การแมปจากสคีมาเป็นชนิดข้อมูล” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การแมปจากสคีมาเป็นชนิดข้อมูล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส TypeScript Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การแมปจากสคีมาเป็นชนิดข้อมูล”

ทำความเข้าใจว่านิยามตารางกลายเป็นชนิดข้อมูล TypeScript ได้อย่างไร คุณปฏิบัติ TypeScript Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน TypeScript Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน TypeScript Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “การแมปจากสคีมาเป็นชนิดข้อมูล” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน TypeScript Academy นี้ได้ไหม

ได้ บทเรียน TypeScript Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การแมปจากสคีมาเป็นชนิดข้อมูล
  2. การสร้างการสืบค้นที่ปลอดภัยด้านชนิดข้อมูล
  3. การอนุมานโครงสร้างผลลัพธ์การสืบค้น
  4. ความสัมพันธ์และการรวมตารางด้วยชนิดข้อมูล
← กลับไปที่ TypeScript Academy