Experte142025-01-15

TypeScript Mapped Types - Type Transformationen

Lerne Mapped Types für dynamische Type-Transformationen. Conditional Types, Template Literals und Advanced Patterns.

#typescript#mapped-types#advanced#conditional

TypeScript Mapped Types - Type Transformationen

Mapped Types transformieren existierende Types in neue Types.

Basic Mapped Types

type Readonly<T> = {
  readonly [K in keyof T]: T[K]
}

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

interface User {
  name: string
  age: number
}

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

type OptionalUser = Optional<User>
// { name?: string; age?: number }

keyof Operator

interface Person {
  name: string
  age: number
  email: string
}

type PersonKeys = keyof Person
// "name" | "age" | "email"

type PersonValues = Person[keyof Person]
// string | number

Mapped Type Syntax

type MappedType<T> = {
  [K in keyof T]: T[K]
}

// Mit Modifiers
type ReadonlyNullable<T> = {
  readonly [K in keyof T]?: T[K] | null
}

// Modifier entfernen
type Required<T> = {
  [K in keyof T]-?: T[K]  // -? entfernt optional
}

type Mutable<T> = {
  -readonly [K in keyof T]: T[K]  // -readonly entfernt readonly
}

Conditional Types

type IsString<T> = T extends string ? true : false

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

// Mit infer
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never

function test(): string {
  return "hello"
}

type T = ReturnType<typeof test>  // string

Conditional Chains

type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  T extends Function ? "function" :
  "object"

type T1 = TypeName<string>  // "string"
type T2 = TypeName<42>      // "number"
type T3 = TypeName<() => void>  // "function"

Template Literal Types

type Greeting = `Hello ${string}`

const a: Greeting = "Hello World"  // ✅ OK
const b: Greeting = "Hi World"     // ❌ Error

// Mit Union Types
type Direction = "top" | "right" | "bottom" | "left"
type Margin = `margin-${Direction}`
// "margin-top" | "margin-right" | "margin-bottom" | "margin-left"

// Kombinationen
type Color = "red" | "blue"
type Size = "sm" | "lg"
type ClassName = `${Color}-${Size}`
// "red-sm" | "red-lg" | "blue-sm" | "blue-lg"

String Manipulation Types

// Built-in
type Uppercase<S extends string> // "HELLO"
type Lowercase<S extends string> // "hello"
type Capitalize<S extends string> // "Hello"
type Uncapitalize<S extends string> // "hELLO"

type Loud = Uppercase<"hello">  // "HELLO"
type Quiet = Lowercase<"WORLD">  // "world"

// Custom
type GetterName<T extends string> = `get${Capitalize<T>}`
type SetterName<T extends string> = `set${Capitalize<T>}`

type UserGetter = GetterName<"name">  // "getName"
type UserSetter = SetterName<"age">   // "setAge"

Advanced Mapped Types

Deep Readonly

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K]
}

interface User {
  name: string
  address: {
    street: string
    city: string
  }
}

type ReadonlyUser = DeepReadonly<User>
// {
//   readonly name: string
//   readonly address: {
//     readonly street: string
//     readonly city: string
//   }
// }

Getters Type

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}

interface User {
  name: string
  age: number
}

type UserGetters = Getters<User>
// {
//   getName: () => string
//   getAge: () => number
// }

Filters

type FilterByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K]
}

interface User {
  name: string
  age: number
  email: string
  isActive: boolean
}

type StringProps = FilterByType<User, string>
// { name: string; email: string }

type NumberProps = FilterByType<User, number>
// { age: number }
Real-World Event System mit Mapped Types
// Event Definitions
interface Events {
  "user:login": { userId: string; timestamp: Date }
  "user:logout": { userId: string }
  "post:create": { postId: string; authorId: string }
  "post:delete": { postId: string }
}

// Event Handler Type
type EventHandler<T> = (data: T) => void | Promise<void>

// Mapped Type für Event Handlers
type EventHandlers = {
  [K in keyof Events]: EventHandler<Events[K]>[]
}

// Event Emitter Class
class EventEmitter {
  private handlers: Partial<EventHandlers> = {}

  on<K extends keyof Events>(
    event: K,
    handler: EventHandler<Events[K]>
  ): void {
    if (!this.handlers[event]) {
      this.handlers[event] = []
    }
    this.handlers[event]!.push(handler)
  }

  emit<K extends keyof Events>(
    event: K,
    data: Events[K]
  ): void {
    const eventHandlers = this.handlers[event]
    if (!eventHandlers) return

    eventHandlers.forEach(handler => handler(data))
  }

  off<K extends keyof Events>(
    event: K,
    handler: EventHandler<Events[K]>
  ): void {
    const eventHandlers = this.handlers[event]
    if (!eventHandlers) return

    const index = eventHandlers.indexOf(handler)
    if (index > -1) {
      eventHandlers.splice(index, 1)
    }
  }
}

// Usage - Vollständig Type-Safe!
const emitter = new EventEmitter()

emitter.on("user:login", (data) => {
  // data ist Type: { userId: string; timestamp: Date }
  console.log(`User ${data.userId} logged in`)
})

emitter.emit("user:login", {
  userId: "123",
  timestamp: new Date()
})

emitter.emit("post:create", {
  postId: "post-1",
  authorId: "user-1"
})

Distributive Conditional Types

type ToArray<T> = T extends any ? T[] : never

type A = ToArray<string | number>
// string[] | number[] (distributed!)

// Non-Distributive mit []
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never

type B = ToArrayNonDist<string | number>
// (string | number)[] (nicht distributed)

Recursive Mapped Types

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object
    ? DeepPartial<T[K]>
    : T[K]
}

interface Config {
  database: {
    host: string
    port: number
    credentials: {
      username: string
      password: string
    }
  }
}

type PartialConfig = DeepPartial<Config>
// Alles optional, auch nested!

📝 Quiz

Was macht der 'as' Clause in Mapped Types?

Tipps & Tricks

Type-Safe Object.keys

function keys<T extends object>(obj: T): (keyof T)[] {
  return Object.keys(obj) as (keyof T)[]
}

const user = { name: "Max", age: 25 }
const userKeys = keys(user)  // ("name" | "age")[]

Exclude Specific Keys

type OmitMultiple<T, K extends keyof T> = {
  [P in Exclude<keyof T, K>]: T[P]
}

interface User {
  id: number
  name: string
  email: string
  password: string
}

type PublicUser = OmitMultiple<User, "password" | "email">
// { id: number; name: string }

Type-Safe Pick

function pick<T, K extends keyof T>(
  obj: T,
  ...keys: K[]
): Pick<T, K> {
  const result = {} as Pick<T, K>
  keys.forEach(key => {
    result[key] = obj[key]
  })
  return result
}

const user = { name: "Max", age: 25, email: "max@test.com" }
const picked = pick(user, "name", "age")
// { name: string; age: number }

Häufige Fehler

Fehler 1: Conditional Type ohne extends

FALSCH:

type Test<T> = T === string ? true : false  // ❌ Error

RICHTIG:

type Test<T> = T extends string ? true : false

Fehler 2: Mapped Type ohne 'in'

FALSCH:

type Test<T> = {
  [K of keyof T]: T[K]  // ❌ Error
}

RICHTIG:

type Test<T> = {
  [K in keyof T]: T[K]
}

Fehler 3: Recursive Type ohne Base Case

UNENDLICH:

type Flatten<T> = T extends any[]
  ? Flatten<T[number]>  // Kein Base Case!
  : T

RICHTIG:

type Flatten<T> = T extends (infer U)[]
  ? U extends any[]
    ? Flatten<U>
    : U
  : T
🎯

Zusammenfassung

Du hast gelernt:

  • ✅ Mapped Types Grundlagen
  • ✅ keyof und in Operator
  • ✅ Conditional Types mit extends
  • ✅ Template Literal Types
  • ✅ Advanced Patterns (Deep Types)
  • ✅ Distributive Conditional Types

Key Takeaways:

  • Mapped Types transformieren Types
  • [K in keyof T] für Iteration
  • Conditional mit extends
  • Template Literals für String-Types
  • Rekursion für Deep Types
  • as Clause für Key Remapping

Nächste Schritte

Mapped Types sind essenziell für:

  • Utility Types Library
  • Type-Safe APIs
  • ORM Frameworks
  • Form Libraries

Viel Erfolg! 🚀

TypeScriptLektion 15 von 15
100% abgeschlossen
Lektion abgeschlossen!

Gut gemacht! 🎉

Du hast "TypeScript Mapped Types - Type Transformationen" abgeschlossen

Artikel bewerten

0.0 (0 Bewertungen)

Bitte einloggen um zu bewerten