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