/** * Advanced TypeScript Types * * This file demonstrates advanced TypeScript features including: * - Generics with constraints * - Conditional types * - Mapped types * - Template literal types * - Recursive types * - Utility type implementations */ // ============================================================================ // Generics Basics // ============================================================================ // Generic function function identity(value: T): T { return value } const stringValue = identity('hello') // Type: string const numberValue = identity(42) // Type: number // Generic interface interface Box { value: T } const stringBox: Box = { value: 'hello' } const numberBox: Box = { value: 42 } // Generic class class Stack { private items: T[] = [] push(item: T): void { this.items.push(item) } pop(): T | undefined { return this.items.pop() } peek(): T | undefined { return this.items[this.items.length - 1] } isEmpty(): boolean { return this.items.length === 0 } } const numberStack = new Stack() numberStack.push(1) numberStack.push(2) numberStack.pop() // Type: number | undefined // ============================================================================ // Generic Constraints // ============================================================================ // Constrain to specific type interface HasLength { length: number } function logLength(item: T): void { console.log(item.length) } logLength('string') // OK logLength([1, 2, 3]) // OK logLength({ length: 10 }) // OK // logLength(42) // Error: number doesn't have length // Constrain to object keys function getProperty(obj: T, key: K): T[K] { return obj[key] } interface User { id: string name: string age: number } const user: User = { id: '1', name: 'Alice', age: 30 } const userName = getProperty(user, 'name') // Type: string // const invalid = getProperty(user, 'invalid') // Error // Multiple type parameters with constraints function merge(obj1: T, obj2: U): T & U { return { ...obj1, ...obj2 } } const merged = merge({ a: 1 }, { b: 2 }) // Type: { a: number } & { b: number } // ============================================================================ // Conditional Types // ============================================================================ // Basic conditional type type IsString = T extends string ? true : false type A = IsString // true type B = IsString // false // Nested conditional types type TypeName = 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" type T2 = TypeName // "number" type T3 = TypeName<() => void> // "function" // Distributive conditional types type ToArray = T extends any ? T[] : never type StrArrOrNumArr = ToArray // string[] | number[] // infer keyword type Flatten = T extends Array ? U : T type Str = Flatten // string type Num = Flatten // number // Return type extraction type MyReturnType = T extends (...args: any[]) => infer R ? R : never function exampleFn(): string { return 'hello' } type ExampleReturn = MyReturnType // string // Parameters extraction type MyParameters = T extends (...args: infer P) => any ? P : never function createUser(name: string, age: number): User { return { id: '1', name, age } } type CreateUserParams = MyParameters // [string, number] // ============================================================================ // Mapped Types // ============================================================================ // Make all properties optional type MyPartial = { [K in keyof T]?: T[K] } interface Person { name: string age: number email: string } type PartialPerson = MyPartial // { // name?: string // age?: number // email?: string // } // Make all properties required type MyRequired = { [K in keyof T]-?: T[K] } // Make all properties readonly type MyReadonly = { readonly [K in keyof T]: T[K] } // Pick specific properties type MyPick = { [P in K]: T[P] } type UserProfile = MyPick // { id: string; name: string } // Omit specific properties type MyOmit = { [P in keyof T as P extends K ? never : P]: T[P] } type UserWithoutAge = MyOmit // { id: string; name: string } // Transform property types type Nullable = { [K in keyof T]: T[K] | null } type NullablePerson = Nullable // { // name: string | null // age: number | null // email: string | null // } // ============================================================================ // Key Remapping // ============================================================================ // Add prefix to keys type Getters = { [K in keyof T as `get${Capitalize}`]: () => T[K] } type PersonGetters = Getters // { // getName: () => string // getAge: () => number // getEmail: () => string // } // Filter keys by type type PickByType = { [K in keyof T as T[K] extends U ? K : never]: T[K] } interface Model { id: number name: string description: string price: number } type StringFields = PickByType // { name: string; description: string } // Remove specific key type RemoveKindField = { [K in keyof T as Exclude]: T[K] } // ============================================================================ // Template Literal Types // ============================================================================ // Event name generation type EventName = `on${Capitalize}` type ClickEvent = EventName<'click'> // "onClick" type SubmitEvent = EventName<'submit'> // "onSubmit" // Combining literals type Color = 'red' | 'green' | 'blue' type Shade = 'light' | 'dark' type ColorShade = `${Shade}-${Color}` // "light-red" | "light-green" | "light-blue" | "dark-red" | "dark-green" | "dark-blue" // CSS properties type CSSProperty = 'margin' | 'padding' type Side = 'top' | 'right' | 'bottom' | 'left' type CSSPropertyWithSide = `${CSSProperty}-${Side}` // "margin-top" | "margin-right" | ... | "padding-left" // Route generation type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' type Endpoint = '/users' | '/products' | '/orders' type ApiRoute = `${HttpMethod} ${Endpoint}` // "GET /users" | "POST /users" | ... | "DELETE /orders" // ============================================================================ // Recursive Types // ============================================================================ // JSON value type type JSONValue = string | number | boolean | null | JSONObject | JSONArray interface JSONObject { [key: string]: JSONValue } interface JSONArray extends Array {} // Tree structure interface TreeNode { value: T children?: TreeNode[] } const tree: TreeNode = { value: 1, children: [ { value: 2, children: [{ value: 4 }, { value: 5 }] }, { value: 3, children: [{ value: 6 }] }, ], } // Deep readonly type DeepReadonly = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly : T[K] } interface NestedConfig { api: { url: string timeout: number } features: { darkMode: boolean } } type ImmutableConfig = DeepReadonly // All properties at all levels are readonly // Deep partial type DeepPartial = { [K in keyof T]?: T[K] extends object ? DeepPartial : T[K] } // ============================================================================ // Advanced Utility Types // ============================================================================ // Exclude types from union type MyExclude = T extends U ? never : T type T4 = MyExclude<'a' | 'b' | 'c', 'a'> // "b" | "c" // Extract types from union type MyExtract = T extends U ? T : never type T5 = MyExtract<'a' | 'b' | 'c', 'a' | 'f'> // "a" // NonNullable type MyNonNullable = T extends null | undefined ? never : T type T6 = MyNonNullable // string // Record type MyRecord = { [P in K]: T } type PageInfo = MyRecord // Awaited type MyAwaited = T extends Promise ? MyAwaited : T type T7 = MyAwaited> // string type T8 = MyAwaited>> // number // ============================================================================ // Branded Types // ============================================================================ type Brand = K & { __brand: T } type USD = Brand type EUR = Brand type UserId = Brand type ProductId = Brand function makeUSD(amount: number): USD { return amount as USD } function makeUserId(id: string): UserId { return id as UserId } const usd = makeUSD(100) const userId = makeUserId('user-123') // Type-safe operations function addMoney(a: USD, b: USD): USD { return (a + b) as USD } // Prevents mixing different branded types // const total = addMoney(usd, eur) // Error // ============================================================================ // Union to Intersection // ============================================================================ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( k: infer I, ) => void ? I : never type Union = { a: string } | { b: number } type Intersection = UnionToIntersection // { a: string } & { b: number } // ============================================================================ // Advanced Generic Patterns // ============================================================================ // Constraining multiple related types function merge< T extends Record, U extends Record, K extends keyof T & keyof U, >(obj1: T, obj2: U, conflictKeys: K[]): T & U { const result = { ...obj1, ...obj2 } conflictKeys.forEach((key) => { // Handle conflicts }) return result as T & U } // Builder pattern with fluent API class QueryBuilder { private selectFields: Set = new Set() select( ...fields: K[] ): QueryBuilder { fields.forEach((field) => this.selectFields.add(field)) return this as any } execute(): Pick { // Execute query return {} as Pick } } // Usage interface Product { id: string name: string price: number description: string } const result = new QueryBuilder() .select('id', 'name') .select('price') .execute() // Type: { id: string; name: string; price: number } // ============================================================================ // Exports // ============================================================================ export type { Box, HasLength, IsString, Flatten, MyPartial, MyRequired, MyReadonly, Nullable, DeepReadonly, DeepPartial, Brand, USD, EUR, UserId, ProductId, JSONValue, TreeNode, } export { Stack, identity, getProperty, merge, makeUSD, makeUserId }