Mapowanie schematu na typ
Dowiedz się, jak definicje tabel stają się typami TypeScript
Mapowanie schematu na typ to bezpłatna lekcja TypeScript Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej TypeScript Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs TypeScript Academy zawiera 4 lekcji w sumie.
Od definicji tabeli do typu wiersza
ORM zapewniający bezpieczeństwo typów pozwala opisać tabelę bazy danych raz i automatycznie wyprowadzić na tej podstawie jej typ wiersza w TypeScript. Nie trzeba ręcznie definiować struktury wiersza — jest ona obliczana na podstawie definicji kolumn.
W tej lekcji jako przykład wykorzystujemy tabelę pgTable w stylu Drizzle.
Definiowanie tabeli
Tabela jest obiektem mapującym nazwy kolumn na buildery kolumn. Każdy builder koduje typ SQL oraz modyfikatory, takie jak notNull lub 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(),
});Inferencja typów kolumn
Każdy builder kolumny zawiera phantom type opisujący wartość, którą generuje. serial i integer mapują się na number, text na string, a boolean na boolean.
Builder śledzi również, czy kolumna dopuszcza wartość 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 steruje nullowalnością
Najważniejszym czynnikiem określającym typ wiersza jest notNull(). Kolumna BEZ tego wywołania staje się w wywnioskowanym typie T | null, ponieważ SQL domyślnie dopuszcza wartość 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 }Modele select i insert
Typ wiersza używany do odczytu (select) różni się od typu używanego do zapisu (insert). Podczas wstawiania kolumny z wartościami domyślnymi lub automatycznym zwiększaniem stają się opcjonalne.
import { InferInsertModel } from "drizzle-orm";
type NewUser = InferInsertModel<typeof users>;
// {
// id?: number; // serial has a default -> optional
// name: string;
// age?: number | null;
// isAdmin: boolean;
// }Jak działa wnioskowanie typów
Wewnętrznie każdy konstruktor kolumn jest klasą generyczną, taką jak PgColumn<{ data: number; notNull: true }>. Typ mapowany przechodzi przez każdy klucz tabeli i odczytuje te flagi.
// 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;
};Typy wyliczeniowe i niestandardowe
Kolumny z unią napisów są mapowane na unie literałów, a nie tylko na typ string. Oznacza to, że nieprawidłowa wartość statusu powoduje błąd kompilacji.
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"
});Wartości domyślne
.default() sprawia, że kolumna jest opcjonalna podczas wstawiania, ale pozostaje obecna podczas odczytu. System typów koduje informację „ma wartość domyślną”, aby zmieniać opcjonalność tylko tam, gdzie jest to właściwe.
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)Dlaczego ma to znaczenie
Ponieważ typ wiersza jest wyprowadzany, zmiana schematu automatycznie aktualizuje typ każdego wyniku zapytania. Zmiana nazwy kolumny powoduje, że kompilator wskazuje każde nieaktualne odwołanie. Nie ma drugiego źródła prawdy, które mogłoby się rozjechać.
Mały przykład od początku do końca
Podsumowując: definicja jest tworzona raz, na jej podstawie wnioskuje się typ, a następnie używa się go jako kontraktu funkcji repozytorium.
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];
}Pułapka: pominięcie notNull
Jeśli zostanie pominięte notNull() przy kolumnie, która w rzeczywistości jest wymagana, wywnioskowany typ stanie się T | null i wymusi zbędne sprawdzanie wartości null w całym kodzie. Schemat powinien wiernie odzwierciedlać rzeczywistość.
Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat mapowania schematu na typ.
Podsumowanie
Dowiedziałeś się, że definicja tabeli jest jedynym źródłem prawdy: konstruktory kolumn przenoszą typy widmowe, typy mapowane przechodzą przez tabelę, aby zbudować typ wiersza, notNull() steruje nullowalnością, a modele select i insert różnią się pod względem opcjonalności. Zmień schemat, a każdy typ wyniku zostanie automatycznie zaktualizowany.
Często zadawane pytania
Czy lekcja „Mapowanie schematu na typ” jest bezpłatna?
Tak — pełny tekst „Mapowanie schematu na typ” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu TypeScript Academy, przejdź na CoddyKit PRO. Kurs TypeScript Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Mapowanie schematu na typ”?
Dowiedz się, jak definicje tabel stają się typami TypeScript Ćwiczysz TypeScript Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć TypeScript Academy?
Nie wymagamy żadnego doświadczenia. TypeScript Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Mapowanie schematu na typ”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji TypeScript Academy?
Tak. Każda lekcja TypeScript Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Mapowanie schematu na typ
- Bezpieczne typowo konstruowanie zapytań
- Wnioskowanie kształtów wyników zapytań
- Relacje i złączenia z typami