Skip to main content
TypeScript Advanced Patterns: Generics, Utility Types und Type Guards

TypeScript Advanced Patterns: Generics, Utility Types und Type Guards

Web DevelopmentDecember 26, 202518 Min. Lesezeit0 Aufrufe
TypeScriptJavaScriptProgrammingTypesBest PracticesTutorial
Teilen:

TypeScript Advanced Patterns: Generics, Utility Types und Type Guards

TypeScript hat die JavaScript-Entwicklung durch statische Typisierung revolutioniert. Aber um seine Kraft wirklich zu nutzen, müssen Sie fortgeschrittene Muster beherrschen. Lassen Sie uns tief in Generika, Utility-Typen und Typenschutz eintauchen.

Warum Advanced TypeScript wichtig ist

TypeScript zu schreiben ist einfach. Um gutes TypeScript zu schreiben, das wartbar, typsicher und elegant ist, müssen Sie fortgeschrittene Muster verstehen. Diese Muster helfen Ihnen:

  • 🎯 Abfangen von Fehlern zur Kompilierungszeit
  • Code-Dokumentation 📚 verbessern
  • Besseres Refactoring 🔄 ermöglichen
  • 💡 Verbessern der automatischen IDE-Vervollständigung

Generics: Einmal schreiben, überall verwenden

Mit Generics können Sie wiederverwendbaren Code schreiben, der mit mehreren Typen funktioniert. ### Basic Generic Function ```typescript function identity<T>(arg: T): T { return arg; }

const num = identity<number>(42); // type: number const str = identity<string>("hello"); // type: string const auto = identity(true); // type inferred: boolean ### Generic Constraintstypescript interface Lengthwise { length: number; }

function logLength<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; }

logLength("hello"); // ✅ Works logLength([1, 2, 3]); // ✅ Works logLength({ length: 10 }); // ✅ Works // logLength(42); // ❌ Error: number doesn't have length ### Generic Interfacestypescript interface Repository<T> { findById(id: string): Promise<T | null>; findAll(): Promise<T[]>; create(item: Omit<T, "id">): Promise<T>; update(id: string, item: Partial<T>): Promise<T>; delete(id: string): Promise<void>; }

interface User { id: string; name: string; email: string; }

class UserRepository implements Repository<User> { async findById(id: string): Promise<User | null> { // Implementation return null; }

async findAll(): Promise<User[]> { return []; }

async create(item: Omit<User, "id">): Promise<User> { const user: User = { id: "generated", ...item }; return user; }

async update(id: string, item: Partial<User>): Promise<User> { // Implementation return { id, name: "", email: "" }; }

async delete(id: string): Promise<void> { // Implementation } } ## Utility Types: TypeScript's Swiss Army Knife ### Partial<T> - Make All Properties Optionaltypescript interface User { id: string; name: string; email: string; age: number; }

function updateUser(id: string, updates: Partial<User>) { // Can update any subset of properties }

updateUser("1", { name: "John" }); // ✅ updateUser("2", { email: "new@email.com" }); // ✅ updateUser("3", { name: "Jane", age: 30 }); // ✅ ### Pick<T, K> - Select Specific Propertiestypescript type UserPreview = Pick<User, "id" | "name">;

const preview: UserPreview = { id: "1", name: "John" // email and age not required }; ### Omit<T, K> - Exclude Specific Propertiestypescript type UserCreate = Omit<User, "id">;

const newUser: UserCreate = { name: "John", email: "john@example.com", age: 30 // id is excluded }; ### Record<K, T> - Create Object Type with Specific Keystypescript type Role = "admin" | "user" | "guest";

type Permissions = Record<Role, string[]>;

const permissions: Permissions = { admin: ["read", "write", "delete"], user: ["read", "write"], guest: ["read"] }; ### ReturnType<T> - Extract Function Return Typetypescript function createUser() { return { id: "123", name: "John", email: "john@example.com" }; }

type User = ReturnType<typeof createUser>; // type User = { id: string; name: string; email: string; } ## Type Guards: Runtime Type Safety ### typeof Type Guardtypescript function processValue(value: string | number) { if (typeof value === "string") { // TypeScript knows value is string here return value.toUpperCase(); } else { // TypeScript knows value is number here return value.toFixed(2); } } ### instanceof Type Guardtypescript class Dog { bark() { console.log("Woof!"); } }

class Cat { meow() { console.log("Meow!"); } }

function makeSound(animal: Dog | Cat) { if (animal instanceof Dog) { animal.bark(); // TypeScript knows it's a Dog } else { animal.meow(); // TypeScript knows it's a Cat } } ### Custom Type Guardstypescript interface Fish { swim: () => void; }

interface Bird { fly: () => void; }

// User-defined type guard function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; }

function move(pet: Fish | Bird) { if (isFish(pet)) { pet.swim(); // TypeScript knows pet is Fish } else { pet.fly(); // TypeScript knows pet is Bird } } ### Discriminated Unionstypescript interface Success { type: "success"; data: any; }

interface Error { type: "error"; message: string; }

type Result = Success | Error;

function handleResult(result: Result) { switch (result.type) { case "success": console.log(result.data); // TypeScript knows it's Success break; case "error": console.log(result.message); // TypeScript knows it's Error break; } } ## Advanced Pattern: Conditional Typestypescript type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true type B = IsString<number>; // false

// More practical example type NonNullable<T> = T extends null | undefined ? never : T;

type A = NonNullable<string | null>; // string type B = NonNullable<number | undefined>; // number ## Advanced Pattern: Mapped Typestypescript type Readonly<T> = { readonly [P in keyof T]: T[P]; };

type Optional<T> = { [P in keyof T]?: T[P]; };

interface User { id: string; name: string; }

type ReadonlyUser = Readonly<User>; // { readonly id: string; readonly name: string; }

type OptionalUser = Optional<User>; // { id?: string; name?: string; } ## Real-World Example: API Response Handlertypescript interface ApiResponse<T> { data?: T; error?: string; status: number; }

// Generic fetch wrapper async function fetchApi<T>(url: string): Promise<ApiResponse<T>> { try { const response = await fetch(url); const data = await response.json();

return {
  data,
  status: response.status
};

} catch (error) { return { error: error instanceof Error ? error.message : "Unknown error", status: 500 }; } }

// Type-safe usage interface User { id: string; name: string; }

async function getUser(id: string) { const response = await fetchApi<User>(/api/users/${id});

if (response.error) { console.error(response.error); return null; }

return response.data; // TypeScript knows this is User | undefined }

2. **Leverage Utility Types**: Don't reinvent the wheel
3. **Create Custom Type Guards**: Improve runtime type safety
4. **Use Discriminated Unions**: For complex state management
5. **Keep Types Simple**: Don't over-engineer

## Common Pitfalls to Avoid

❌ **Too Generic**
```typescript
function process<T>(data: T): T {
  // Too generic, loses type information
  return data;
}

Properly Constrained

Loading code...

Conclusion

Advanced TypeScript patterns transform your code from "typed JavaScript" to truly type-safe, maintainable applications. Master these patterns, and you'll write better code with fewer bugs.

Start small, practice often, and gradually incorporate these patterns into your projects . Your future self (and teammates) will thank you! 🚀

Lass uns in Kontakt treten

Bereit, gemeinsam etwas Großartiges zu schaffen?

Sende uns eine Nachricht

🚀

Lass uns chatten.

Erzähl mir von deinem Projekt.

Lass uns gemeinsam etwas schaffen 🤘

Address

Faisalabad, Pakistan

Visit my social profile and get connected