Problem

Datenmodelle in TypeScript (TS) definieren ist super simpel – ein Interface oder Type ist schnell erstellt. Gut und schön. Aber wie wir wissen, nutzt das nur zur Kompilier-Zeit etwas. Was aber, wenn ich Laufzeit-kompatible Definitionen meiner Daten haben möchte? Das kann relevant werden, um Formulareingaben von Nutzer*innen zu validieren oder Payloads zu parsen, welche ich aus einem anderen System bekommen habe. In der Vergangenheit konnte man sich behelfen, indem man redundante Definitionen pflegen musste (ajv, joi), zwingend mit Klassen arbeiten musste (class-validator) oder aus "nicht-TS"-Schema-Definitionen TS-Dateien generiert werden mussten (openapi).

Lösung

Seit einiger Zeit gibt es eine Lib mit Namen zod. Diese erlaubt es, TS-Typen aus den definierten Schemas zu "inferieren". Das reduziert die Redundanz bei der Schema-Definition auf ein Minimum und erlaubt es, die Schemas zur Validierung bzw. zum Parsen zu benutzen.

Beispiel

Von primitiven Typen bis hin zu komplexen, beliebig zusammengesetzten Objekten kann man alles definieren, was das TS-Herz begehrt. Ein alltägliches Beispiel könnte so aussehen:
const UserRoleSchema = z.enum( [ 'admin' , 'employee' , 'owner' ]) ;
type UserRole = z.infer<typeof UserRoleSchema>;

const UserSchema = z.object( {
  id: z.string( ) ,
  email: z.string( ) .email( ) ,
  fullName: z.string( ) ,
  role: UserRoleSchema ,
} ) :
type User = z.infer<typeof UserSchema> ;
  
const AddUserRequestSchema = UserSchema.omit( { id: true } ) ;
type AddUserRequest = z.infer<typeof AddUserRequestSchema>;
Die mit z.infer erstellten Typen kann man direkt benutzen. Um Objekt-Schemas wiederzuverwenden, gibt es eine umfangreiche API, für die das omit exemplarisch stehen soll. Dieses verhält sich wie der zugehörige TS Utility Type.
Beim Parsen anhand des Schemas werden die TS Types automatisch inferiert.
try {
   const response = await httpClient.get<User>( 'users/me' ) ;
   return await UserSchema.parseAsync (response.data) ;
}  catch (e) {
   console.error (e) ;
}
Es gibt sowohl async Methoden als auch synchrone. In beiden Varianten gibt es "safe" Methoden, welche keine Fehler werfen.
const parsedState = AddUserRequestSchema.safeParse ( state ) ;
if ( parsedState.success ) {
   doSomething ( parsedState.data ) ;
}
jambit ToiletPaper#175 TypeScript-Models definieren mit Zod

Weitere Aspekte

  • Das Ökosystem ist groß! Es gibt einige ähnliche Libs, die der Autor von zod auch dokumentiert: https://github.com/colinhacks/zod#comparison
  • zod versucht sich an best practices zu orientieren, daher gibt es eine Unterscheidung zwischen z.enum und z.nativeEnum, wobei Ersteres eindeutig empfohlen wird.

---

SCHREIB UNS

* Pflichtfeld

SCHREIB UNS

* Pflichtfeld

Cookie-Einstellungen

Diese Website verwendet Cookies, um Inhalte und Anzeigen zu personalisieren, Funktionen für soziale Medien anbieten zu können und Zugriffe auf die Website zu analysieren. Zudem werden Informationen zu Ihrer Verwendung der Website an Partner für soziale Medien, Werbung und Analysen weitergegeben. Die Partner führen diese Informationen möglicherweise mit weiteren Daten zusammen, die Sie ihnen bereitgestellt haben oder die sie im Rahmen Ihrer Nutzung der Dienste gesammelt haben.

Weitere Informationen finden Sie in unserer Datenschutzerklärung. Dort können Sie nachträglich auch Ihre Cookie-Einstellungen ändern.

contact icon

Kontakt aufnehmen