0Pricing
TypeScript Academy · Leçon

Correspondance entre schéma et type

Comprendre comment les définitions de tables deviennent des types TypeScript.

Correspondance entre schéma et type est une leçon TypeScript Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage TypeScript Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours TypeScript Academy comprend 4 leçons au total.

De la définition d’une table au type de ligne

Un ORM garantissant la sûreté des types vous permet de décrire une table de base de données une seule fois et d’en dériver automatiquement le type de ligne TypeScript. Vous n’écrivez jamais manuellement la structure de la ligne ; elle est calculée à partir des définitions de colonnes.

Dans cette leçon, nous utiliserons une table pgTable de style Drizzle comme exemple fil conducteur.

Définir une table

Une table est un objet qui associe des noms de colonnes à des générateurs de colonnes. Chaque générateur encode un type SQL ainsi que des modificateurs comme notNull ou 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(),
});

Inférence du type des colonnes

Chaque générateur de colonne porte un type fantôme qui décrit la valeur qu’il produit. serial et integer correspondent à number, text à string et boolean à boolean.

Le générateur suit également le fait que la colonne peut ou non contenir la valeur nulle.

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 contrôle la nullabilité

Le facteur qui détermine le plus le type de ligne est notNull(). Une colonne WITHOUT celui-ci devient T | null dans le type inféré, car SQL autorise NULL par défaut.

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 }

Modèles de sélection et d’insert

Le type de ligne pour la lecture (select) diffère du type utilisé pour l’écriture (insert). Lors d’un insert, les colonnes qui possèdent des valeurs par défaut ou qui s’auto-incrémentent deviennent optionnelles.

import { InferInsertModel } from "drizzle-orm";

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

Fonctionnement de l’inférence

En interne, chaque constructeur de colonne est une classe générique comme PgColumn<{ data: number; notNull: true }>. Un type mappé parcourt chaque clé de la table et lit ces indicateurs.

// 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;
};

Énumérations et types personnalisés

Les colonnes formées d’une union de chaînes correspondent à des unions littérales, et pas simplement à string. Ainsi, une valeur d’état invalide provoque une erreur de compilation.

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"
});

Valeurs par défaut

.default() rend une colonne optionnelle lors d’un insert, tout en la conservant dans les sélections. Le système de types encode le fait qu’elle « possède une valeur par défaut », afin de ne rendre la colonne optionnelle qu’au moment approprié.

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)

Pourquoi est-ce important

Comme le type de ligne est dérivé, une modification du schéma met automatiquement à jour le type de chaque résultat de requête. Renommez une colonne et le compilateur signale chaque référence obsolète. Il n’existe pas de seconde source de vérité susceptible de diverger.

Un petit exemple de bout en bout

Pour tout réunir : définissez une fois, inférez le type, puis utilisez-le comme contrat pour une fonction de dépôt.

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];
}

Piège&nbsp;: oublier notNull

Si vous oubliez notNull() sur une colonne qui est réellement obligatoire, le type inféré devient T | null et impose des vérifications de nullité inutiles dans tout votre code. Gardez un schéma fidèle à la réalité.

Vérification rapide

Testez votre compréhension de la correspondance entre schéma et type.

Récapitulatif

Vous avez appris qu’une définition de table est l’unique source de vérité : les constructeurs de colonnes portent des types fantômes, les types mappés parcourent la table pour construire le type de ligne, notNull() contrôle la nullabilité, et les modèles de sélection et d’insert diffèrent par le caractère optionnel des champs. Modifiez le schéma et chaque type de résultat se met automatiquement à jour.

Questions Fréquemment Posées

La leçon « Correspondance entre schéma et type » est-elle gratuite ?

Oui — le texte complet de « Correspondance entre schéma et type » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours TypeScript Academy, passe à CoddyKit PRO. Le cours TypeScript Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Correspondance entre schéma et type » ?

Comprendre comment les définitions de tables deviennent des types TypeScript. Tu pratiques TypeScript Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer TypeScript Academy ?

Aucune expérience préalable n'est requise. TypeScript Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « Correspondance entre schéma et type » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon TypeScript Academy ?

Oui. Chaque leçon TypeScript Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Correspondance entre schéma et type
  2. Construction de requêtes sûre du point de vue du typage
  3. Inférer la structure des résultats de requêtes
  4. Relations et jointures avec les types
← Retour à TypeScript Academy