Lesson 04-TypeScript Advanced Applications

Advanced Type Operations

Index Types

Application of the keyof Operator:

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

// Get a union type of all object keys
type PersonKeys = keyof Person // "name" | "age" | "address"

// Use Case 1: Restricting Property Access
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const person: Person = { name: 'Alice', age: 30, address: '123 Main St' }
const name = getProperty(person, 'name') // Correct
// const unknown = getProperty(person, 'unknown') // Error: Argument of type '"unknown"' is not assignable to parameter of type '"name" | "age" | "address"'

// Use Case 2: Basis for Mapped Types
type OptionalPerson = {
  [K in keyof Person]?: Person[K] // Equivalent to { name?: string; age?: number; address?: string }
}

Advanced Indexed Access Types:

interface User {
  id: number
  name: string
  profile: {
    email: string
    age: number
  }
}

// Deep Indexed Access
type ProfileEmailType = User['profile']['email'] // string

// Combined with Conditional Types
type ValueOf<T> = T[keyof T]
type PersonValues = ValueOf<Person> // string | number

// Utility Type: Get Function Return Type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any
type GreetReturn = ReturnType<() => string> // string

Mapped Types

Basic Mapped Types:

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

// Make all properties optional
type PartialPerson = {
  [K in keyof Person]?: Person[K]
}

// Make all properties readonly
type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K]
}

// Change all property types to string
type StringifiedPerson = {
  [K in keyof Person]: string
}

Advanced Mapped Types:

// Conditional Mapping: Filter Specific Type Properties
type StringKeys<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K]
}

type PersonStringProps = StringKeys<Person> // { name: string; address: string }

// Key Name Transformation: Add Prefix
type AddPrefix<T, Prefix extends string> = {
  [K in keyof T as `${Prefix}${Capitalize<string & K>}`]: T[K]
}

type PrefixedPerson = AddPrefix<Person, 'user'> // { userName: string; userAge: number; userAddress: string }

// Property Filtering
type FilterProperties<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K]
}

type NumberProps = FilterProperties<Person, number> // { age: number }

Conditional Types

Basic Conditional Types:

// Simple Conditional Type
type IsString<T> = T extends string ? true : false
type A = IsString<'hello'> // true
type B = IsString<123> // false

// Distributed Conditional Types (when T is a union type)
type ToArray<T> = T extends any ? T[] : never
type StrOrNumArray = ToArray<string | number> // string[] | number[]

// Non-Distributed Conditional Types (using square brackets)
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type StrOrNumArrayNonDist = ToArrayNonDist<string | number> // (string | number)[]

Application of the infer Keyword:

// Extract Function Return Type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any
type GreetReturn = ReturnType<() => string> // string

// Extract Array Element Type
type ElementType<T> = T extends (infer U)[] ? U : T
type NumArrayElement = ElementType<number[]> // number
type StrElement = ElementType<string> // string

// Extract Promise Generic Type
type UnpackPromise<T> = T extends Promise<infer U> ? U : T
type PromiseString = UnpackPromise<Promise<string>> // string

// Recursively Unpack Nested Promises
type DeepUnpackPromise<T> = T extends Promise<infer U> ? DeepUnpackPromise<U> : T
type DeepPromise = DeepUnpackPromise<Promise<Promise<Promise<string>>>> // string

Type Inference (infer)

Complex Type Inference Cases:

// Extract Function Parameter Types
type Parameters<T> = T extends (...args: infer P) => any ? P : never
type GreetParams = Parameters<() => void> // []
type AddParams = Parameters<(a: number, b: number) => number> // [number, number]

// Extract Constructor Parameters
type ConstructorParameters<T extends new (...args: any) => any> = 
  T extends new (...args: infer P) => any ? P : never

class Person {
  constructor(public name: string, public age: number) {}
}
type PersonCtorParams = ConstructorParameters<typeof Person> // [string, number]

// Extract Class Instance Type
type InstanceType<T extends new (...args: any) => any> = 
  T extends new (...args: any) => infer R ? R : any

type PersonInstance = InstanceType<typeof Person> // Person

// Recursive Handling of Nested Structures
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}

interface NestedObject {
  a: number
  b: {
    c: string
    d: {
      e: boolean
    }
  }
}

type PartialNested = DeepPartial<NestedObject>

Template Literal Types

String Template Types:

// Basic Template Literal
type EventName = 'click' | 'scroll' | 'mousemove'
type HandlerName = `on${Capitalize<EventName>}`
// "onClick" | "onScroll" | "onMousemove"

// Dynamic API Paths
type ApiRoute = '/api' | '/user' | '/product'
type FullApiPath = `/v1${ApiRoute}`
// "/v1/api" | "/v1/user" | "/v1/product"

// CSS Unit Conversion
type PxValue = `${number}px`
type PercentValue = `${number}%`
type CssUnit = PxValue | PercentValue
// "1px" | "2px" | ... | "100px" | "0%" | "1%" | ... | "100%"

// Combining Multiple Templates
type Direction = 'North' | 'South' | 'East' | 'West'
type CompassPoint = `to${Capitalize<Direction>}`
// "toNorth" | "toSouth" | "toEast" | "toWest"

Advanced Template Applications:

// Database Field Type Mapping
type FieldType = 'string' | 'number' | 'boolean'
type ColumnDef<T extends FieldType> = `column_${T}`
type StringColumn = ColumnDef<'string'> // "column_string"
type NumberColumn = ColumnDef<'number'> // "column_number"

// RESTful Method Types
type HttpMethod = 'get' | 'post' | 'put' | 'delete'
type ApiMethod = `_${HttpMethod}`
type GetMethod = ApiMethod // "_get" | "_post" | "_put" | "_delete"

// State Machine Transitions
type State = 'idle' | 'loading' | 'success' | 'error'
type Transition<T extends State> = `${T}_to_${State}`
type IdleToLoading = Transition<'idle'> // "idle_to_idle" | "idle_to_loading" | "idle_to_success" | "idle_to_error"

Share your love