Extending & Merging
Lerne wie du Interfaces erweitern und Typen kombinieren kannst in TypeScript.
Aktualisiert:
Extending & Merging
Das Erweitern und Kombinieren von Typen ist ein mächtiges Feature in TypeScript. Es ermöglicht dir, bestehende Typen wiederzuverwenden und zu spezialisieren.
Interface Extending
Einfaches Extending
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
const dog: Dog = {
name: "Buddy",
age: 3,
breed: "Golden Retriever",
bark() {
console.log("Woof!");
}
};
Mehrfaches Extending
interface Identifiable {
id: number;
}
interface Timestamped {
createdAt: Date;
updatedAt: Date;
}
interface Nameable {
name: string;
}
interface User extends Identifiable, Timestamped, Nameable {
email: string;
role: "admin" | "user";
}
const user: User = {
id: 1,
createdAt: new Date(),
updatedAt: new Date(),
name: "Max",
email: "max@example.com",
role: "admin"
};
Property Override
Du kannst Properties überschreiben (mit kompatiblem Typ):
interface Base {
value: string | number;
}
interface Specific extends Base {
value: number; // Eingeschränkt auf number
}
const item: Specific = {
value: 42 // Muss number sein
};
Type Intersection
Types werden mit & kombiniert:
type Identifiable = {
id: number;
};
type Timestamped = {
createdAt: Date;
updatedAt: Date;
};
type User = Identifiable & Timestamped & {
name: string;
email: string;
};
const user: User = {
id: 1,
createdAt: new Date(),
updatedAt: new Date(),
name: "Max",
email: "max@example.com"
};
Intersection mit Interfaces
Types und Interfaces können kombiniert werden:
interface Person {
name: string;
age: number;
}
type Employee = Person & {
employeeId: string;
department: string;
};
const employee: Employee = {
name: "Max",
age: 30,
employeeId: "E001",
department: "Engineering"
};
Declaration Merging
Nur Interfaces können gemergt werden:
// Erste Deklaration
interface Config {
apiUrl: string;
}
// Zweite Deklaration (wird gemergt)
interface Config {
timeout: number;
}
// Dritte Deklaration
interface Config {
retries: number;
}
// Config hat jetzt alle Properties
const config: Config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
};
Globale Interface-Erweiterung
Nützlich für Library-Erweiterungen:
// Erweitere Window
declare global {
interface Window {
analytics: {
track: (event: string, data?: object) => void;
};
}
}
// Jetzt typsicher
window.analytics.track("page_view", { path: "/" });
Express Request erweitern
declare global {
namespace Express {
interface Request {
user?: {
id: number;
email: string;
};
}
}
}
// In Middleware
app.use((req, res, next) => {
req.user = { id: 1, email: "test@example.com" };
next();
});
Module Augmentation
Typen aus Modulen erweitern:
// original-module.ts
export interface Theme {
primary: string;
secondary: string;
}
// augmentation.ts
declare module "./original-module" {
interface Theme {
accent: string;
background: string;
}
}
// Jetzt hat Theme alle vier Properties
Utility Types für Extending
Pick - Properties auswählen
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
type PublicUser = Pick<User, "id" | "name" | "email">;
// { id: number; name: string; email: string; }
Omit - Properties ausschließen
type UserWithoutPassword = Omit<User, "password">;
// { id: number; name: string; email: string; createdAt: Date; }
Partial - Alles optional
type UpdateUserInput = Partial<User>;
// Alle Properties sind optional
Required - Alles required
interface Config {
apiUrl?: string;
timeout?: number;
}
type RequiredConfig = Required<Config>;
// { apiUrl: string; timeout: number; }
Kombinieren
type CreateUserInput = Omit<User, "id" | "createdAt">;
type UpdateUserInput = Partial<CreateUserInput>;
// CreateUserInput: { name: string; email: string; password: string; }
// UpdateUserInput: { name?: string; email?: string; password?: string; }
Praktische Patterns
Base Entity Pattern
interface BaseEntity {
id: number;
createdAt: Date;
updatedAt: Date;
}
interface User extends BaseEntity {
name: string;
email: string;
}
interface Post extends BaseEntity {
title: string;
content: string;
authorId: number;
}
interface Comment extends BaseEntity {
text: string;
postId: number;
authorId: number;
}
Request/Response Types
interface BaseRequest {
requestId: string;
timestamp: Date;
}
interface AuthenticatedRequest extends BaseRequest {
userId: number;
token: string;
}
interface CreateUserRequest extends AuthenticatedRequest {
payload: {
name: string;
email: string;
};
}
// Response Types
interface BaseResponse {
success: boolean;
requestId: string;
}
interface DataResponse<T> extends BaseResponse {
success: true;
data: T;
}
interface ErrorResponse extends BaseResponse {
success: false;
error: {
code: string;
message: string;
};
}
type ApiResponse<T> = DataResponse<T> | ErrorResponse;
State Machine Types
interface BaseState {
timestamp: Date;
}
interface IdleState extends BaseState {
status: "idle";
}
interface LoadingState extends BaseState {
status: "loading";
progress?: number;
}
interface SuccessState<T> extends BaseState {
status: "success";
data: T;
}
interface ErrorState extends BaseState {
status: "error";
error: Error;
}
type AsyncState<T> = IdleState | LoadingState | SuccessState<T> | ErrorState;
function handleState<T>(state: AsyncState<T>): string {
switch (state.status) {
case "idle":
return "Waiting...";
case "loading":
return `Loading... ${state.progress ?? 0}%`;
case "success":
return `Got data: ${JSON.stringify(state.data)}`;
case "error":
return `Error: ${state.error.message}`;
}
}
Mixin Pattern
type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
createdAt = new Date();
updatedAt = new Date();
};
}
function Identifiable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
id = Math.random().toString(36).slice(2);
};
}
class BaseUser {
name: string;
constructor(name: string) {
this.name = name;
}
}
const EnhancedUser = Timestamped(Identifiable(BaseUser));
const user = new EnhancedUser("Max");
console.log(user.id, user.createdAt, user.name);
Zusammenfassung
Interface Extending:
extendsfür Vererbung- Mehrfach-Extends möglich
- Property Override mit kompatiblem Typ
Type Intersection:
&für Kombinationen- Funktioniert mit Types und Interfaces
Declaration Merging:
- Nur für Interfaces
- Nützlich für Library-Erweiterungen
Utility Types:
Pick,Omitfür Property-AuswahlPartial,Requiredfür Optionalität
Im nächsten Modul lernst du Klassen in TypeScript!