Mappatura da schema a tipo
Scopra come le definizioni delle tabelle diventano tipi TypeScript.
Mappatura da schema a tipo è una lezione TypeScript Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento TypeScript Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso TypeScript Academy include 4 lezioni in totale.
Dalla definizione della tabella al tipo della riga
Un ORM con sicurezza dei tipi consente di descrivere una tabella del database una sola volta e di derivarne automaticamente il tipo della riga in TypeScript. Non è mai necessario scrivere manualmente la struttura della riga: viene calcolata a partire dalle definizioni delle colonne.
In questa lezione useremo una pgTable in stile Drizzle come esempio guida.
Definire una tabella
Una tabella è un oggetto che associa i nomi delle colonne ai builder delle colonne. Ogni builder codifica un tipo SQL e modificatori come notNull o 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(),
});Inferenza dei tipi delle colonne
Ogni builder di colonna contiene un tipo phantom che descrive il valore prodotto. serial e integer corrispondono a number, text a string e boolean a boolean.
Il builder tiene inoltre traccia del fatto che la colonna possa essere nullable.
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 controlla la nullabilità
Il fattore più importante per determinare il tipo della riga è notNull(). Una colonna che ne è priva diventa T | null nel tipo inferito, perché SQL consente NULL per impostazione predefinita.
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 }Modelli di select e insert
Il tipo della riga per la lettura (select) è diverso dal tipo per la scrittura (insert). Durante l'insert, le colonne con valori predefiniti o incremento automatico diventano facoltative.
import { InferInsertModel } from "drizzle-orm";
type NewUser = InferInsertModel<typeof users>;
// {
// id?: number; // serial has a default -> optional
// name: string;
// age?: number | null;
// isAdmin: boolean;
// }Come funziona l'inferenza
Internamente, ogni column builder è una classe generica come PgColumn<{ data: number; notNull: true }>. Un mapped type percorre ogni chiave della tabella e legge quei flag.
// 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 e tipi personalizzati
Le colonne con unione di stringhe vengono mappate a unioni di valori letterali, non semplicemente a string. Questo significa che un valore di stato non valido causa un errore di compilazione.
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"
});Valori predefiniti
.default() rende una colonna facoltativa durante l'insert, ma la mantiene presente durante la select. Il sistema dei tipi codifica "has default", così può rendere facoltative le colonne solo quando appropriato.
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)Perché è importante
Poiché il tipo della riga è derivato, una modifica allo schema aggiorna automaticamente il tipo di ogni risultato delle query. Rinomini una colonna e il compilatore segnalerà ogni riferimento obsoleto. Non esiste una seconda fonte di verità che possa divergere.
Un piccolo esempio end-to-end
In sintesi: definisca una volta sola, inferisca il tipo e lo utilizzi come contratto per una funzione repository.
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];
}Problema: dimenticare notNull
Se dimentica notNull() su una colonna che in realtà è obbligatoria, il tipo inferito diventa T | null e impone controlli null non necessari in tutto il codice. Mantenga lo schema coerente con la realtà.
Verifica rapida
Verifichi la Sua comprensione della mappatura da schema a tipo.
Riepilogo
Ha imparato che la definizione di una tabella è la singola fonte di verità: i column builder trasportano phantom type, i mapped type percorrono la tabella per costruire il tipo della riga, notNull() controlla la nullabilità e i modelli di select e insert differiscono per la facoltatività. Modifichi lo schema e ogni tipo di risultato si aggiornerà automaticamente.
Domande Frequenti
La lezione «Mappatura da schema a tipo» è gratuita?
Sì — il testo completo di «Mappatura da schema a tipo» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso TypeScript Academy, passa a CoddyKit PRO. Il corso TypeScript Academy include 4 lezioni in totale.
Cosa imparerò in «Mappatura da schema a tipo»?
Scopra come le definizioni delle tabelle diventano tipi TypeScript. Eserciti TypeScript Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare TypeScript Academy?
Non è richiesta alcuna esperienza precedente. TypeScript Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.
Quanto tempo richiede la lezione «Mappatura da schema a tipo»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione TypeScript Academy?
Sì. Ogni lezione TypeScript Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Mappatura da schema a tipo
- Costruzione di query type-safe
- Inferire la forma dei risultati delle query
- Relazioni e join con i tipi