advanced-types.ts raw

   1  /**
   2   * Advanced TypeScript Types
   3   *
   4   * This file demonstrates advanced TypeScript features including:
   5   * - Generics with constraints
   6   * - Conditional types
   7   * - Mapped types
   8   * - Template literal types
   9   * - Recursive types
  10   * - Utility type implementations
  11   */
  12  
  13  // ============================================================================
  14  // Generics Basics
  15  // ============================================================================
  16  
  17  // Generic function
  18  function identity<T>(value: T): T {
  19    return value
  20  }
  21  
  22  const stringValue = identity('hello') // Type: string
  23  const numberValue = identity(42) // Type: number
  24  
  25  // Generic interface
  26  interface Box<T> {
  27    value: T
  28  }
  29  
  30  const stringBox: Box<string> = { value: 'hello' }
  31  const numberBox: Box<number> = { value: 42 }
  32  
  33  // Generic class
  34  class Stack<T> {
  35    private items: T[] = []
  36  
  37    push(item: T): void {
  38      this.items.push(item)
  39    }
  40  
  41    pop(): T | undefined {
  42      return this.items.pop()
  43    }
  44  
  45    peek(): T | undefined {
  46      return this.items[this.items.length - 1]
  47    }
  48  
  49    isEmpty(): boolean {
  50      return this.items.length === 0
  51    }
  52  }
  53  
  54  const numberStack = new Stack<number>()
  55  numberStack.push(1)
  56  numberStack.push(2)
  57  numberStack.pop() // Type: number | undefined
  58  
  59  // ============================================================================
  60  // Generic Constraints
  61  // ============================================================================
  62  
  63  // Constrain to specific type
  64  interface HasLength {
  65    length: number
  66  }
  67  
  68  function logLength<T extends HasLength>(item: T): void {
  69    console.log(item.length)
  70  }
  71  
  72  logLength('string') // OK
  73  logLength([1, 2, 3]) // OK
  74  logLength({ length: 10 }) // OK
  75  // logLength(42) // Error: number doesn't have length
  76  
  77  // Constrain to object keys
  78  function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  79    return obj[key]
  80  }
  81  
  82  interface User {
  83    id: string
  84    name: string
  85    age: number
  86  }
  87  
  88  const user: User = { id: '1', name: 'Alice', age: 30 }
  89  const userName = getProperty(user, 'name') // Type: string
  90  // const invalid = getProperty(user, 'invalid') // Error
  91  
  92  // Multiple type parameters with constraints
  93  function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
  94    return { ...obj1, ...obj2 }
  95  }
  96  
  97  const merged = merge({ a: 1 }, { b: 2 }) // Type: { a: number } & { b: number }
  98  
  99  // ============================================================================
 100  // Conditional Types
 101  // ============================================================================
 102  
 103  // Basic conditional type
 104  type IsString<T> = T extends string ? true : false
 105  
 106  type A = IsString<string> // true
 107  type B = IsString<number> // false
 108  
 109  // Nested conditional types
 110  type TypeName<T> = T extends string
 111    ? 'string'
 112    : T extends number
 113      ? 'number'
 114      : T extends boolean
 115        ? 'boolean'
 116        : T extends undefined
 117          ? 'undefined'
 118          : T extends Function
 119            ? 'function'
 120            : 'object'
 121  
 122  type T1 = TypeName<string> // "string"
 123  type T2 = TypeName<number> // "number"
 124  type T3 = TypeName<() => void> // "function"
 125  
 126  // Distributive conditional types
 127  type ToArray<T> = T extends any ? T[] : never
 128  
 129  type StrArrOrNumArr = ToArray<string | number> // string[] | number[]
 130  
 131  // infer keyword
 132  type Flatten<T> = T extends Array<infer U> ? U : T
 133  
 134  type Str = Flatten<string[]> // string
 135  type Num = Flatten<number> // number
 136  
 137  // Return type extraction
 138  type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never
 139  
 140  function exampleFn(): string {
 141    return 'hello'
 142  }
 143  
 144  type ExampleReturn = MyReturnType<typeof exampleFn> // string
 145  
 146  // Parameters extraction
 147  type MyParameters<T> = T extends (...args: infer P) => any ? P : never
 148  
 149  function createUser(name: string, age: number): User {
 150    return { id: '1', name, age }
 151  }
 152  
 153  type CreateUserParams = MyParameters<typeof createUser> // [string, number]
 154  
 155  // ============================================================================
 156  // Mapped Types
 157  // ============================================================================
 158  
 159  // Make all properties optional
 160  type MyPartial<T> = {
 161    [K in keyof T]?: T[K]
 162  }
 163  
 164  interface Person {
 165    name: string
 166    age: number
 167    email: string
 168  }
 169  
 170  type PartialPerson = MyPartial<Person>
 171  // {
 172  //   name?: string
 173  //   age?: number
 174  //   email?: string
 175  // }
 176  
 177  // Make all properties required
 178  type MyRequired<T> = {
 179    [K in keyof T]-?: T[K]
 180  }
 181  
 182  // Make all properties readonly
 183  type MyReadonly<T> = {
 184    readonly [K in keyof T]: T[K]
 185  }
 186  
 187  // Pick specific properties
 188  type MyPick<T, K extends keyof T> = {
 189    [P in K]: T[P]
 190  }
 191  
 192  type UserProfile = MyPick<User, 'id' | 'name'>
 193  // { id: string; name: string }
 194  
 195  // Omit specific properties
 196  type MyOmit<T, K extends keyof T> = {
 197    [P in keyof T as P extends K ? never : P]: T[P]
 198  }
 199  
 200  type UserWithoutAge = MyOmit<User, 'age'>
 201  // { id: string; name: string }
 202  
 203  // Transform property types
 204  type Nullable<T> = {
 205    [K in keyof T]: T[K] | null
 206  }
 207  
 208  type NullablePerson = Nullable<Person>
 209  // {
 210  //   name: string | null
 211  //   age: number | null
 212  //   email: string | null
 213  // }
 214  
 215  // ============================================================================
 216  // Key Remapping
 217  // ============================================================================
 218  
 219  // Add prefix to keys
 220  type Getters<T> = {
 221    [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
 222  }
 223  
 224  type PersonGetters = Getters<Person>
 225  // {
 226  //   getName: () => string
 227  //   getAge: () => number
 228  //   getEmail: () => string
 229  // }
 230  
 231  // Filter keys by type
 232  type PickByType<T, U> = {
 233    [K in keyof T as T[K] extends U ? K : never]: T[K]
 234  }
 235  
 236  interface Model {
 237    id: number
 238    name: string
 239    description: string
 240    price: number
 241  }
 242  
 243  type StringFields = PickByType<Model, string>
 244  // { name: string; description: string }
 245  
 246  // Remove specific key
 247  type RemoveKindField<T> = {
 248    [K in keyof T as Exclude<K, 'kind'>]: T[K]
 249  }
 250  
 251  // ============================================================================
 252  // Template Literal Types
 253  // ============================================================================
 254  
 255  // Event name generation
 256  type EventName<T extends string> = `on${Capitalize<T>}`
 257  
 258  type ClickEvent = EventName<'click'> // "onClick"
 259  type SubmitEvent = EventName<'submit'> // "onSubmit"
 260  
 261  // Combining literals
 262  type Color = 'red' | 'green' | 'blue'
 263  type Shade = 'light' | 'dark'
 264  type ColorShade = `${Shade}-${Color}`
 265  // "light-red" | "light-green" | "light-blue" | "dark-red" | "dark-green" | "dark-blue"
 266  
 267  // CSS properties
 268  type CSSProperty = 'margin' | 'padding'
 269  type Side = 'top' | 'right' | 'bottom' | 'left'
 270  type CSSPropertyWithSide = `${CSSProperty}-${Side}`
 271  // "margin-top" | "margin-right" | ... | "padding-left"
 272  
 273  // Route generation
 274  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
 275  type Endpoint = '/users' | '/products' | '/orders'
 276  type ApiRoute = `${HttpMethod} ${Endpoint}`
 277  // "GET /users" | "POST /users" | ... | "DELETE /orders"
 278  
 279  // ============================================================================
 280  // Recursive Types
 281  // ============================================================================
 282  
 283  // JSON value type
 284  type JSONValue = string | number | boolean | null | JSONObject | JSONArray
 285  
 286  interface JSONObject {
 287    [key: string]: JSONValue
 288  }
 289  
 290  interface JSONArray extends Array<JSONValue> {}
 291  
 292  // Tree structure
 293  interface TreeNode<T> {
 294    value: T
 295    children?: TreeNode<T>[]
 296  }
 297  
 298  const tree: TreeNode<number> = {
 299    value: 1,
 300    children: [
 301      { value: 2, children: [{ value: 4 }, { value: 5 }] },
 302      { value: 3, children: [{ value: 6 }] },
 303    ],
 304  }
 305  
 306  // Deep readonly
 307  type DeepReadonly<T> = {
 308    readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
 309  }
 310  
 311  interface NestedConfig {
 312    api: {
 313      url: string
 314      timeout: number
 315    }
 316    features: {
 317      darkMode: boolean
 318    }
 319  }
 320  
 321  type ImmutableConfig = DeepReadonly<NestedConfig>
 322  // All properties at all levels are readonly
 323  
 324  // Deep partial
 325  type DeepPartial<T> = {
 326    [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
 327  }
 328  
 329  // ============================================================================
 330  // Advanced Utility Types
 331  // ============================================================================
 332  
 333  // Exclude types from union
 334  type MyExclude<T, U> = T extends U ? never : T
 335  
 336  type T4 = MyExclude<'a' | 'b' | 'c', 'a'> // "b" | "c"
 337  
 338  // Extract types from union
 339  type MyExtract<T, U> = T extends U ? T : never
 340  
 341  type T5 = MyExtract<'a' | 'b' | 'c', 'a' | 'f'> // "a"
 342  
 343  // NonNullable
 344  type MyNonNullable<T> = T extends null | undefined ? never : T
 345  
 346  type T6 = MyNonNullable<string | null | undefined> // string
 347  
 348  // Record
 349  type MyRecord<K extends keyof any, T> = {
 350    [P in K]: T
 351  }
 352  
 353  type PageInfo = MyRecord<string, number>
 354  
 355  // Awaited
 356  type MyAwaited<T> = T extends Promise<infer U> ? MyAwaited<U> : T
 357  
 358  type T7 = MyAwaited<Promise<string>> // string
 359  type T8 = MyAwaited<Promise<Promise<number>>> // number
 360  
 361  // ============================================================================
 362  // Branded Types
 363  // ============================================================================
 364  
 365  type Brand<K, T> = K & { __brand: T }
 366  
 367  type USD = Brand<number, 'USD'>
 368  type EUR = Brand<number, 'EUR'>
 369  type UserId = Brand<string, 'UserId'>
 370  type ProductId = Brand<string, 'ProductId'>
 371  
 372  function makeUSD(amount: number): USD {
 373    return amount as USD
 374  }
 375  
 376  function makeUserId(id: string): UserId {
 377    return id as UserId
 378  }
 379  
 380  const usd = makeUSD(100)
 381  const userId = makeUserId('user-123')
 382  
 383  // Type-safe operations
 384  function addMoney(a: USD, b: USD): USD {
 385    return (a + b) as USD
 386  }
 387  
 388  // Prevents mixing different branded types
 389  // const total = addMoney(usd, eur) // Error
 390  
 391  // ============================================================================
 392  // Union to Intersection
 393  // ============================================================================
 394  
 395  type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
 396    k: infer I,
 397  ) => void
 398    ? I
 399    : never
 400  
 401  type Union = { a: string } | { b: number }
 402  type Intersection = UnionToIntersection<Union>
 403  // { a: string } & { b: number }
 404  
 405  // ============================================================================
 406  // Advanced Generic Patterns
 407  // ============================================================================
 408  
 409  // Constraining multiple related types
 410  function merge<
 411    T extends Record<string, any>,
 412    U extends Record<string, any>,
 413    K extends keyof T & keyof U,
 414  >(obj1: T, obj2: U, conflictKeys: K[]): T & U {
 415    const result = { ...obj1, ...obj2 }
 416    conflictKeys.forEach((key) => {
 417      // Handle conflicts
 418    })
 419    return result as T & U
 420  }
 421  
 422  // Builder pattern with fluent API
 423  class QueryBuilder<T, Selected extends keyof T = never> {
 424    private selectFields: Set<keyof T> = new Set()
 425  
 426    select<K extends keyof T>(
 427      ...fields: K[]
 428    ): QueryBuilder<T, Selected | K> {
 429      fields.forEach((field) => this.selectFields.add(field))
 430      return this as any
 431    }
 432  
 433    execute(): Pick<T, Selected> {
 434      // Execute query
 435      return {} as Pick<T, Selected>
 436    }
 437  }
 438  
 439  // Usage
 440  interface Product {
 441    id: string
 442    name: string
 443    price: number
 444    description: string
 445  }
 446  
 447  const result = new QueryBuilder<Product>()
 448    .select('id', 'name')
 449    .select('price')
 450    .execute()
 451  // Type: { id: string; name: string; price: number }
 452  
 453  // ============================================================================
 454  // Exports
 455  // ============================================================================
 456  
 457  export type {
 458    Box,
 459    HasLength,
 460    IsString,
 461    Flatten,
 462    MyPartial,
 463    MyRequired,
 464    MyReadonly,
 465    Nullable,
 466    DeepReadonly,
 467    DeepPartial,
 468    Brand,
 469    USD,
 470    EUR,
 471    UserId,
 472    ProductId,
 473    JSONValue,
 474    TreeNode,
 475  }
 476  
 477  export { Stack, identity, getProperty, merge, makeUSD, makeUserId }
 478  
 479