Mapped Types
Lerne Mapped Types in TypeScript für dynamische Typ-Transformationen.
Aktualisiert:
Mapped Types
Mapped Types ermöglichen es dir, bestehende Typen zu transformieren, indem du über ihre Properties iterierst. Sie sind eines der mächtigsten Features für fortgeschrittene Typisierung.
Grundlagen
Die Syntax basiert auf Index Signatures:
type MappedType = {
[Key in UnionOfKeys]: ValueType;
};
Einfaches Beispiel
type Keys = "name" | "age" | "email";
// Alle Keys werden zu string
type StringRecord = {
[K in Keys]: string;
};
// Ergebnis:
// {
// name: string;
// age: string;
// email: string;
// }
keyof und Mapped Types
Mit keyof kannst du über die Keys eines bestehenden Typs iterieren:
type User = {
id: number;
name: string;
email: string;
};
// Alle Properties werden zu boolean
type UserFlags = {
[K in keyof User]: boolean;
};
// Ergebnis:
// {
// id: boolean;
// name: boolean;
// email: boolean;
// }
Zugriff auf den Original-Typ
Mit T[K] greifst du auf den Original-Typ zu:
type User = {
id: number;
name: string;
email: string;
};
// Kopiert den Typ (identity mapping)
type CopyOfUser = {
[K in keyof User]: User[K];
};
// Macht alle Properties optional
type PartialUser = {
[K in keyof User]?: User[K];
};
// Macht alle Properties readonly
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
Generische Mapped Types
Die wahre Kraft liegt in generischen Mapped Types:
// Partial - macht alle Properties optional
type Partial<T> = {
[K in keyof T]?: T[K];
};
// Required - macht alle Properties required
type Required<T> = {
[K in keyof T]-?: T[K]; // -? entfernt optional
};
// Readonly - macht alle Properties readonly
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// Mutable - entfernt readonly
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
Verwendung
type User = {
id: number;
name: string;
email?: string;
};
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }
type RequiredUser = Required<User>;
// { id: number; name: string; email: string; }
type ReadonlyUser = Readonly<User>;
// { readonly id: number; readonly name: string; readonly email?: string; }
Pick und Omit
Wähle bestimmte Properties aus:
// Pick - nur bestimmte Keys behalten
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// Omit - bestimmte Keys ausschließen
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// Verwendung
type User = {
id: number;
name: string;
email: string;
password: string;
};
type PublicUser = Pick<User, "id" | "name" | "email">;
// { id: number; name: string; email: string; }
type UserWithoutPassword = Omit<User, "password">;
// { id: number; name: string; email: string; }
Record Type
Erstellt einen Typ mit bestimmten Keys und Value-Typ:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
// Verwendung
type PageInfo = {
title: string;
url: string;
};
type Pages = "home" | "about" | "contact";
type Site = Record<Pages, PageInfo>;
// {
// home: PageInfo;
// about: PageInfo;
// contact: PageInfo;
// }
const site: Site = {
home: { title: "Home", url: "/" },
about: { title: "About", url: "/about" },
contact: { title: "Contact", url: "/contact" }
};
Key Remapping (as clause)
Transformiere Keys während des Mappings:
type User = {
name: string;
age: number;
email: string;
};
// Prefix hinzufügen
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
// {
// getName: () => string;
// getAge: () => number;
// getEmail: () => string;
// }
// Setters
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
type UserSetters = Setters<User>;
// {
// setName: (value: string) => void;
// setAge: (value: number) => void;
// setEmail: (value: string) => void;
// }
Keys filtern
// Nur Properties eines bestimmten Typs behalten
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
type User = {
id: number;
name: string;
email: string;
age: number;
};
type StringPropsOfUser = OnlyStrings<User>;
// { name: string; email: string; }
// Nur required Properties
type OnlyRequired<T> = {
[K in keyof T as undefined extends T[K] ? never : K]: T[K];
};
Praktische Mapped Types
DeepPartial
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object
? DeepPartial<T[K]>
: T[K];
};
type Config = {
database: {
host: string;
port: number;
credentials: {
username: string;
password: string;
};
};
server: {
port: number;
};
};
type PartialConfig = DeepPartial<Config>;
// Alle nested Properties sind optional
DeepReadonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
const config: DeepReadonly<Config> = {
database: {
host: "localhost",
port: 5432,
credentials: {
username: "admin",
password: "secret"
}
},
server: { port: 3000 }
};
// config.database.port = 3306; // Fehler: readonly
Nullable
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
type User = {
name: string;
email: string;
};
type NullableUser = Nullable<User>;
// { name: string | null; email: string | null; }
Event Handlers
type Events = {
click: { x: number; y: number };
focus: { target: string };
blur: { target: string };
};
type EventHandlers = {
[K in keyof Events as `on${Capitalize<K>}`]: (event: Events[K]) => void;
};
// {
// onClick: (event: { x: number; y: number }) => void;
// onFocus: (event: { target: string }) => void;
// onBlur: (event: { target: string }) => void;
// }
const handlers: EventHandlers = {
onClick: (e) => console.log(`Click at ${e.x}, ${e.y}`),
onFocus: (e) => console.log(`Focus on ${e.target}`),
onBlur: (e) => console.log(`Blur from ${e.target}`)
};
Form State
type FormField<T> = {
value: T;
touched: boolean;
error?: string;
};
type FormState<T> = {
[K in keyof T]: FormField<T[K]>;
};
type UserForm = {
name: string;
email: string;
age: number;
};
type UserFormState = FormState<UserForm>;
// {
// name: FormField<string>;
// email: FormField<string>;
// age: FormField<number>;
// }
const formState: UserFormState = {
name: { value: "", touched: false },
email: { value: "", touched: false },
age: { value: 0, touched: false }
};
Kombination mit Conditional Types
type NonFunctionProperties<T> = {
[K in keyof T as T[K] extends Function ? never : K]: T[K];
};
type FunctionProperties<T> = {
[K in keyof T as T[K] extends Function ? K : never]: T[K];
};
type User = {
id: number;
name: string;
greet: () => string;
save: () => Promise<void>;
};
type UserData = NonFunctionProperties<User>;
// { id: number; name: string; }
type UserMethods = FunctionProperties<User>;
// { greet: () => string; save: () => Promise<void>; }
Zusammenfassung
| Mapped Type | Beschreibung |
|---|---|
Partial<T> | Alle Properties optional |
Required<T> | Alle Properties required |
Readonly<T> | Alle Properties readonly |
Pick<T, K> | Nur bestimmte Properties |
Omit<T, K> | Properties ausschließen |
Record<K, T> | Objekt mit bestimmten Keys |
Key Features:
keyof Tfür alle Keys eines TypsT[K]für den Typ einer Propertyasfür Key-Transformation+/-für Modifier hinzufügen/entfernen
Im nächsten Kapitel lernst du Conditional Types!