FoundryKit

Type Definitions Reference

Complete reference for all FoundryKit type definitions

Type Definitions Reference

Complete reference for all FoundryKit type definitions, organized by category with detailed examples and usage patterns.

Utility Types

Core Utility Types

Essential utility types for data manipulation and transformation.

Optional<T, K>

Make specific properties of a type optional.

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>

// Usage
interface User {
  id: string
  name: string
  email: string
  avatar: string
  bio: string
}

// Make avatar and bio optional
type UserWithOptionalFields = Optional<User, 'avatar' | 'bio'>
// Result: { id: string; name: string; email: string; avatar?: string; bio?: string }

// Example usage
function createUser(userData: UserWithOptionalFields): User {
  return {
    avatar: '/default-avatar.png',
    bio: '',
    ...userData
  }
}

Required<T, K>

Make specific properties of a type required.

type Required<T, K extends keyof T> = T & Required<Pick<T, K>>

// Usage
interface PartialUser {
  id?: string
  name?: string
  email?: string
  avatar?: string
}

// Make id and name required
type UserWithRequiredFields = Required<PartialUser, 'id' | 'name'>
// Result: { id: string; name: string; email?: string; avatar?: string }

Nullable<T>

Allow a type to be null.

type Nullable<T> = T | null

// Usage
type NullableString = Nullable<string> // string | null
type NullableUser = Nullable<User> // User | null

function findUser(id: string): NullableUser {
  const user = users.find(u => u.id === id)
  return user || null
}

NonNullable<T>

Remove null and undefined from a type.

type NonNullable<T> = T extends null | undefined ? never : T

// Usage
type DefiniteString = NonNullable<string | null | undefined> // string
type DefiniteUser = NonNullable<User | null | undefined> // User

function processUser(user: User | null | undefined) {
  if (user) {
    // TypeScript knows user is NonNullable<User> here
    handleDefiniteUser(user)
  }
}

function handleDefiniteUser(user: NonNullable<User>) {
  // user is guaranteed to be defined
  console.log(user.name)
}

Deep Utility Types

Advanced utility types for nested object manipulation.

DeepPartial<T>

Make all properties of a type optional recursively.

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

// Usage
interface NestedConfig {
  app: {
    name: string
    version: string
    features: {
      darkMode: boolean
      analytics: boolean
    }
  }
  user: {
    profile: {
      name: string
      email: string
    }
  }
}

type PartialConfig = DeepPartial<NestedConfig>
// Result: All properties are optional at every level

const config: PartialConfig = {
  app: {
    name: 'MyApp' // version and features are optional
  }
  // user is completely optional
}

DeepRequired<T>

Make all properties of a type required recursively.

type DeepRequired<T> = {
  [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P]
}

// Usage
type RequiredConfig = DeepRequired<PartialConfig>
// Result: All properties are required at every level

DeepReadonly<T>

Make all properties of a type readonly recursively.

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

// Usage
type ReadonlyConfig = DeepReadonly<NestedConfig>
// Result: All properties are readonly at every level

const config: ReadonlyConfig = {
  app: {
    name: 'MyApp',
    version: '1.0.0',
    features: {
      darkMode: true,
      analytics: false
    }
  },
  user: {
    profile: {
      name: 'John',
      email: 'john@example.com'
    }
  }
}

// config.app.name = 'NewApp' // Error: Cannot assign to 'name' because it is a read-only property

Type Extraction Utilities

Extract specific types from complex type structures.

KeysOfType<T, U>

Extract keys of a type that match a specific type.

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

// Usage
interface UserData {
  id: string
  name: string
  age: number
  isActive: boolean
  createdAt: Date
  tags: string[]
}

type StringKeys = KeysOfType<UserData, string> // 'id' | 'name'
type NumberKeys = KeysOfType<UserData, number> // 'age'
type BooleanKeys = KeysOfType<UserData, boolean> // 'isActive'
type DateKeys = KeysOfType<UserData, Date> // 'createdAt'
type ArrayKeys = KeysOfType<UserData, any[]> // 'tags'

// Example usage
function updateStringField<K extends StringKeys>(
  user: UserData,
  key: K,
  value: string
): UserData {
  return { ...user, [key]: value }
}

PickByType<T, U>

Pick properties of a type that match a specific type.

type PickByType<T, U> = Pick<T, KeysOfType<T, U>>

// Usage
type StringFields = PickByType<UserData, string>
// Result: { id: string; name: string }

type NumberFields = PickByType<UserData, number>
// Result: { age: number }

OmitByType<T, U>

Omit properties of a type that match a specific type.

type OmitByType<T, U> = Omit<T, KeysOfType<T, U>>

// Usage
type NonStringFields = OmitByType<UserData, string>
// Result: { age: number; isActive: boolean; createdAt: Date; tags: string[] }

Prettify Type

Clean up complex intersection types for better readability.

Prettify<T>

Flatten intersection types for better TypeScript IntelliSense.

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

// Usage
type BaseProps = {
  id: string
  className?: string
}

type ExtendedProps = {
  title: string
  description?: string
}

type UtilityProps = {
  onClick?: () => void
  disabled?: boolean
}

// Without Prettify - shows as intersection
type ComplexProps = BaseProps & ExtendedProps & UtilityProps

// With Prettify - shows as single object type
type CleanProps = Prettify<BaseProps & ExtendedProps & UtilityProps>
// Result: {
//   id: string
//   className?: string
//   title: string
//   description?: string
//   onClick?: () => void
//   disabled?: boolean
// }

React Types

Component Types

Comprehensive React component type definitions.

ComponentProps<T>

Extract props from HTML elements or React components.

type ComponentProps<T extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>> =
  T extends React.JSXElementConstructor<infer P>
    ? P
    : T extends keyof JSX.IntrinsicElements
    ? JSX.IntrinsicElements[T]
    : never

// Usage
type ButtonProps = ComponentProps<'button'>
// Result: React.ButtonHTMLAttributes<HTMLButtonElement>

type InputProps = ComponentProps<'input'>
// Result: React.InputHTMLAttributes<HTMLInputElement>

// Custom component props
const CustomButton = ({ variant, ...props }: { variant: string } & ButtonProps) => {
  return <button {...props} className={`btn btn-${variant}`} />
}

type CustomButtonProps = ComponentProps<typeof CustomButton>
// Result: { variant: string } & React.ButtonHTMLAttributes<HTMLButtonElement>

ComponentRef<T>

Extract ref type from HTML elements or React components.

type ComponentRef<T extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>> =
  T extends keyof JSX.IntrinsicElements
    ? JSX.IntrinsicElements[T] extends React.DetailedHTMLProps<any, infer E>
      ? E
      : never
    : T extends React.JSXElementConstructor<any>
    ? React.ComponentRef<T>
    : never

// Usage
type ButtonRef = ComponentRef<'button'> // HTMLButtonElement
type InputRef = ComponentRef<'input'> // HTMLInputElement
type DivRef = ComponentRef<'div'> // HTMLDivElement

// Example usage
const Button = forwardRef<ButtonRef, ButtonProps>((props, ref) => {
  return <button ref={ref} {...props} />
})

ForwardRefComponent<T, P>

Type for components created with forwardRef.

type ForwardRefComponent<
  T extends keyof JSX.IntrinsicElements,
  P = {}
> = React.ForwardRefExoticComponent<
  React.PropsWithoutRef<P & ComponentProps<T>> & React.RefAttributes<ComponentRef<T>>
>

// Usage
interface CustomButtonProps {
  variant?: 'primary' | 'secondary'
  size?: 'sm' | 'md' | 'lg'
}

const CustomButton: ForwardRefComponent<'button', CustomButtonProps> = forwardRef(
  ({ variant = 'primary', size = 'md', className, ...props }, ref) => {
    return (
      <button
        ref={ref}
        className={cn('btn', `btn-${variant}`, `btn-${size}`, className)}
        {...props}
      />
    )
  }
)

PolymorphicComponent<T, P>

Type for polymorphic components that can render as different elements.

type PolymorphicComponent<
  T extends keyof JSX.IntrinsicElements,
  P = {}
> = <C extends keyof JSX.IntrinsicElements = T>(
  props: {
    as?: C
  } & P & ComponentProps<C>
) => React.ReactElement | null

// Usage
interface CardProps {
  variant?: 'default' | 'elevated'
  padding?: 'sm' | 'md' | 'lg'
}

const Card: PolymorphicComponent<'div', CardProps> = ({
  as: Component = 'div',
  variant = 'default',
  padding = 'md',
  className,
  children,
  ...props
}) => {
  return (
    <Component
      className={cn('card', `card-${variant}`, `card-padding-${padding}`, className)}
      {...props}
    >
      {children}
    </Component>
  )
}

// Usage examples
<Card>Default div card</Card>
<Card as="section">Section card</Card>
<Card as="article" className="custom-class">Article card</Card>

Common Component Props

Frequently used component prop patterns.

WithChildren

Add children prop to a component.

type WithChildren<T = {}> = T & {
  children?: React.ReactNode
}

// Usage
interface CardProps extends WithChildren {
  title?: string
  variant?: 'default' | 'elevated'
}

function Card({ title, variant = 'default', children }: CardProps) {
  return (
    <div className={`card card-${variant}`}>
      {title && <div className="card-header">{title}</div>}
      <div className="card-content">{children}</div>
    </div>
  )
}

WithClassName

Add className prop to a component.

type WithClassName<T = {}> = T & {
  className?: string
}

// Usage
interface ButtonProps extends WithClassName {
  variant?: 'primary' | 'secondary'
  disabled?: boolean
}

function Button({ variant = 'primary', disabled, className, ...props }: ButtonProps) {
  return (
    <button
      className={cn('btn', `btn-${variant}`, { 'btn-disabled': disabled }, className)}
      disabled={disabled}
      {...props}
    />
  )
}

WithStyle

Add style prop to a component.

type WithStyle<T = {}> = T & {
  style?: React.CSSProperties
}

// Usage
interface ContainerProps extends WithChildren, WithClassName, WithStyle {
  maxWidth?: string | number
}

function Container({ maxWidth, style, className, children }: ContainerProps) {
  return (
    <div
      className={cn('container', className)}
      style={{ maxWidth, ...style }}
    >
      {children}
    </div>
  )
}

Event Types

Event Handler Types

Type-safe event handling for React components.

EventHandler<T>

Generic event handler type.

type EventHandler<T = Event> = (event: T) => void

// Usage
interface ButtonProps {
  onClick?: EventHandler<MouseEvent>
  onKeyDown?: EventHandler<KeyboardEvent>
  onFocus?: EventHandler<FocusEvent>
}

function Button({ onClick, onKeyDown, onFocus, ...props }: ButtonProps) {
  return (
    <button
      onClick={onClick}
      onKeyDown={onKeyDown}
      onFocus={onFocus}
      {...props}
    />
  )
}

ChangeHandler<T>

Handler for input change events with typed value.

type ChangeHandler<T = string> = (value: T, event: React.ChangeEvent<HTMLInputElement>) => void

// Usage
interface InputProps {
  value: string
  onChange: ChangeHandler<string>
  onNumberChange?: ChangeHandler<number>
}

function Input({ value, onChange, onNumberChange }: InputProps) {
  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const newValue = event.target.value
    onChange(newValue, event)
    
    if (onNumberChange) {
      const numValue = parseFloat(newValue)
      if (!isNaN(numValue)) {
        onNumberChange(numValue, event)
      }
    }
  }

  return <input value={value} onChange={handleChange} />
}

SubmitHandler<T>

Handler for form submission with typed data.

type SubmitHandler<T = unknown> = (data: T, event: React.FormEvent) => void | Promise<void>

// Usage
interface FormData {
  name: string
  email: string
  age: number
}

interface FormProps {
  onSubmit: SubmitHandler<FormData>
  initialData?: Partial<FormData>
}

function UserForm({ onSubmit, initialData }: FormProps) {
  const [formData, setFormData] = useState<FormData>({
    name: '',
    email: '',
    age: 0,
    ...initialData
  })

  const handleSubmit = (event: React.FormEvent) => {
    event.preventDefault()
    onSubmit(formData, event)
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
    </form>
  )
}

Specific Event Handlers

Pre-defined handlers for common events.

type MouseHandler = EventHandler<React.MouseEvent>
type KeyboardHandler = EventHandler<React.KeyboardEvent>
type FocusHandler = EventHandler<React.FocusEvent>
type TouchHandler = EventHandler<React.TouchEvent>

// Usage
interface InteractiveElementProps {
  onMouseEnter?: MouseHandler
  onMouseLeave?: MouseHandler
  onKeyDown?: KeyboardHandler
  onKeyUp?: KeyboardHandler
  onFocus?: FocusHandler
  onBlur?: FocusHandler
  onTouchStart?: TouchHandler
  onTouchEnd?: TouchHandler
}

API Types

Request and Response Types

Standardized API communication types.

ApiResponse<T>

Standard API response wrapper.

interface ApiResponse<T = unknown> {
  data: T
  success: boolean
  message?: string
  timestamp: string
  requestId?: string
}

// Usage
interface User {
  id: string
  name: string
  email: string
}

type UserResponse = ApiResponse<User>
type UsersResponse = ApiResponse<User[]>

async function fetchUser(id: string): Promise<UserResponse> {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}

ApiError

Standard API error structure.

interface ApiError {
  error: true
  code: string
  message: string
  details?: Record<string, any>
  timestamp: string
  path?: string
}

// Usage
interface ValidationError extends ApiError {
  code: 'VALIDATION_ERROR'
  details: {
    field: string
    rule: string
    value: any
  }
}

interface NotFoundError extends ApiError {
  code: 'NOT_FOUND'
  details: {
    resource: string
    id: string
  }
}

// Error handling
async function handleApiCall<T>(apiCall: () => Promise<T>): Promise<T> {
  try {
    return await apiCall()
  } catch (error) {
    if (isApiError(error)) {
      console.error(`API Error [${error.code}]: ${error.message}`)
      throw error
    }
    throw new Error('Unknown API error')
  }
}

function isApiError(error: unknown): error is ApiError {
  return typeof error === 'object' && error !== null && 'error' in error
}

PaginatedResponse<T>

Response type for paginated data.

interface PaginationMeta {
  page: number
  limit: number
  total: number
  totalPages: number
  hasNextPage: boolean
  hasPrevPage: boolean
}

interface PaginatedResponse<T = unknown> extends ApiResponse<T[]> {
  pagination: PaginationMeta
}

// Usage
type PaginatedUsers = PaginatedResponse<User>

async function fetchUsers(page = 1, limit = 10): Promise<PaginatedUsers> {
  const response = await fetch(`/api/users?page=${page}&limit=${limit}`)
  return response.json()
}

// Usage in component
function UsersList() {
  const [users, setUsers] = useState<PaginatedUsers | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetchUsers().then(setUsers).finally(() => setLoading(false))
  }, [])

  if (loading) return <div>Loading...</div>
  if (!users) return <div>No users found</div>

  return (
    <div>
      {users.data.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
      <div>
        Page {users.pagination.page} of {users.pagination.totalPages}
      </div>
    </div>
  )
}

ApiEndpoint<TRequest, TResponse>

Type definition for API endpoints.

type ApiEndpoint<TRequest = unknown, TResponse = unknown> = {
  request: TRequest
  response: TResponse
}

// Usage
interface UserEndpoints {
  getUser: ApiEndpoint<{ id: string }, ApiResponse<User>>
  getUsers: ApiEndpoint<{ page?: number; limit?: number }, PaginatedResponse<User>>
  createUser: ApiEndpoint<CreateUserRequest, ApiResponse<User>>
  updateUser: ApiEndpoint<{ id: string } & UpdateUserRequest, ApiResponse<User>>
  deleteUser: ApiEndpoint<{ id: string }, { success: boolean }>
}

// Type-safe API client
class UserApiClient {
  async getUser(
    params: UserEndpoints['getUser']['request']
  ): Promise<UserEndpoints['getUser']['response']> {
    const response = await fetch(`/api/users/${params.id}`)
    return response.json()
  }

  async getUsers(
    params: UserEndpoints['getUsers']['request'] = {}
  ): Promise<UserEndpoints['getUsers']['response']> {
    const searchParams = new URLSearchParams()
    if (params.page) searchParams.set('page', String(params.page))
    if (params.limit) searchParams.set('limit', String(params.limit))
    
    const response = await fetch(`/api/users?${searchParams}`)
    return response.json()
  }
}

Form Types

Form Data and Validation

Comprehensive form handling types.

FormField<T, V>

Type for individual form fields with validation rules.

interface FormFieldValidation {
  required?: boolean
  minLength?: number
  maxLength?: number
  min?: number
  max?: number
  pattern?: string | RegExp
  custom?: (value: any) => boolean | string
}

interface FormField<T = any, V extends FormFieldValidation = {}> {
  value: T
  validation?: V
  error?: string
  touched?: boolean
  dirty?: boolean
}

// Usage
interface UserFormFields {
  name: FormField<string, { required: true; minLength: 2; maxLength: 50 }>
  email: FormField<string, { required: true; pattern: 'email' }>
  age: FormField<number, { required: true; min: 18; max: 120 }>
  bio: FormField<string, { maxLength: 500 }>
}

FormData<T>

Extract form data from form field definitions.

type FormData<T extends Record<string, FormField<any, any>>> = {
  [K in keyof T]: T[K] extends FormField<infer U, any> ? U : never
}

// Usage
type UserFormData = FormData<UserFormFields>
// Result: {
//   name: string
//   email: string
//   age: number
//   bio: string
// }

ValidationResult<T>

Result of form validation.

interface ValidationResult<T = unknown> {
  isValid: boolean
  errors: FormErrors<T>
  data: T | null
}

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

// Usage
interface UserValidationResult extends ValidationResult<UserFormData> {
  isValid: boolean
  errors: FormErrors<UserFormData>
  data: UserFormData | null
}

function validateUserForm(data: UserFormData): UserValidationResult {
  const errors: FormErrors<UserFormData> = {}
  
  if (!data.name || data.name.length < 2) {
    errors.name = 'Name must be at least 2 characters'
  }
  
  if (!data.email || !isValidEmail(data.email)) {
    errors.email = 'Please enter a valid email address'
  }
  
  if (!data.age || data.age < 18 || data.age > 120) {
    errors.age = 'Age must be between 18 and 120'
  }
  
  const isValid = Object.keys(errors).length === 0
  
  return {
    isValid,
    errors,
    data: isValid ? data : null
  }
}

FieldValidator<T>

Type for field validation functions.

interface FieldValidationResult {
  isValid: boolean
  error?: string
}

type FieldValidator<T = any> = (value: T) => FieldValidationResult

// Usage
const emailValidator: FieldValidator<string> = (value) => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  if (!emailRegex.test(value)) {
    return { isValid: false, error: 'Please enter a valid email address' }
  }
  return { isValid: true }
}

const ageValidator: FieldValidator<number> = (value) => {
  if (value < 18) {
    return { isValid: false, error: 'You must be at least 18 years old' }
  }
  if (value > 120) {
    return { isValid: false, error: 'Please enter a valid age' }
  }
  return { isValid: true }
}

// Compose validators
function composeValidators<T>(...validators: FieldValidator<T>[]): FieldValidator<T> {
  return (value: T) => {
    for (const validator of validators) {
      const result = validator(value)
      if (!result.isValid) {
        return result
      }
    }
    return { isValid: true }
  }
}

const nameValidator = composeValidators<string>(
  (value) => value.length >= 2 ? { isValid: true } : { isValid: false, error: 'Too short' },
  (value) => value.length <= 50 ? { isValid: true } : { isValid: false, error: 'Too long' }
)

Type Guards and Predicates

Runtime Type Checking

Type guards for runtime type validation.

TypeGuard<T>

Generic type guard function type.

type TypeGuard<T> = (value: unknown) => value is T

// Usage
const isString: TypeGuard<string> = (value): value is string => {
  return typeof value === 'string'
}

const isNumber: TypeGuard<number> = (value): value is number => {
  return typeof value === 'number' && !isNaN(value)
}

const isObject: TypeGuard<Record<string, unknown>> = (value): value is Record<string, unknown> => {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

// Complex type guard
const isUser: TypeGuard<User> = (value): value is User => {
  return (
    isObject(value) &&
    isString(value.id) &&
    isString(value.name) &&
    isString(value.email)
  )
}

// Usage
function processUserData(data: unknown) {
  if (isUser(data)) {
    // TypeScript knows data is User here
    console.log(`Processing user: ${data.name}`)
    console.log(`Email: ${data.email}`)
  } else {
    console.error('Invalid user data')
  }
}

Predicate<T>

Generic predicate function type.

type Predicate<T> = (value: T) => boolean

// Usage
const isAdult: Predicate<User> = (user) => {
  return user.age >= 18
}

const isActiveUser: Predicate<User> = (user) => {
  return user.isActive && user.lastLoginAt > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
}

// Compose predicates
function and<T>(...predicates: Predicate<T>[]): Predicate<T> {
  return (value: T) => predicates.every(predicate => predicate(value))
}

function or<T>(...predicates: Predicate<T>[]): Predicate<T> {
  return (value: T) => predicates.some(predicate => predicate(value))
}

const isActiveAdult = and(isAdult, isActiveUser)

// Filter users
const activeAdultUsers = users.filter(isActiveAdult)

Advanced Types

Conditional and Mapped Types

Advanced TypeScript type manipulation.

If<C, T, F>

Conditional type helper.

type If<C extends boolean, T, F> = C extends true ? T : F

// Usage
type IsStringArray<T> = T extends string[] ? true : false
type StringArrayResult = If<IsStringArray<string[]>, 'yes', 'no'> // 'yes'
type NumberArrayResult = If<IsStringArray<number[]>, 'yes', 'no'> // 'no'

DeepKeyOf<T>

Get all possible deep keys of an object type.

type DeepKeyOf<T> = T extends object
  ? {
      [K in keyof T]: K extends string
        ? T[K] extends object
          ? K | `${K}.${DeepKeyOf<T[K]>}`
          : K
        : never
    }[keyof T]
  : never

// Usage
interface NestedObject {
  user: {
    profile: {
      name: string
      settings: {
        theme: string
        notifications: boolean
      }
    }
  }
  app: {
    version: string
  }
}

type DeepKeys = DeepKeyOf<NestedObject>
// Result: "user" | "app" | "user.profile" | "user.profile.name" | 
//         "user.profile.settings" | "user.profile.settings.theme" | 
//         "user.profile.settings.notifications" | "app.version"

PathValue<T, P>

Get the type of a value at a specific path.

type PathValue<T, P extends string> = P extends keyof T
  ? T[P]
  : P extends `${infer K}.${infer R}`
  ? K extends keyof T
    ? PathValue<T[K], R>
    : never
  : never

// Usage
type UserName = PathValue<NestedObject, 'user.profile.name'> // string
type Theme = PathValue<NestedObject, 'user.profile.settings.theme'> // string
type Notifications = PathValue<NestedObject, 'user.profile.settings.notifications'> // boolean

Next Steps