FoundryKit

Getting Started

Get started with FoundryKit types for type-safe development

Getting Started

Learn how to use FoundryKit types to build type-safe applications with comprehensive TypeScript support.

Installation

Install the Types Package

Install the FoundryKit types package in your project.

npm install @foundrykit/types

Verify Installation

Check that the package is properly installed and accessible.

npm list @foundrykit/types

Prerequisites

Before using FoundryKit types, ensure you have the following:

  • TypeScript (v4.9 or higher) - for type checking and inference
  • Node.js (v18 or higher)
  • React (v18 or higher) - for React-specific types

Check Your Environment

# Check TypeScript version
npx tsc --version

# Check Node.js version
node --version

# Check React version (if using React types)
npm list react

Quick Start

Basic Usage

Import and use types in your TypeScript project.

import type {
  // Common utility types
  Optional,
  Required,
  Nullable,
  
  // React component types
  ComponentProps,
  ComponentRef,
  
  // Event types
  EventHandler,
  ChangeHandler,
  
  // API types
  ApiResponse,
  ApiError,
  
  // Form types
  FormData,
  ValidationResult
} from '@foundrykit/types'

// Use types in your components
interface UserProfileProps {
  user: Optional<User, 'avatar' | 'bio'>
  onUpdate: EventHandler<User>
  className?: string
}

export function UserProfile({ user, onUpdate, className }: UserProfileProps) {
  // Type-safe component implementation
  return (
    <div className={className}>
      <h1>{user.name}</h1>
      {user.avatar && <img src={user.avatar} alt="Avatar" />}
      {user.bio && <p>{user.bio}</p>}
    </div>
  )
}

TypeScript Configuration

Configure TypeScript for optimal type checking with FoundryKit types.

{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    
    // Strict type checking
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,
    
    // Path mapping for cleaner imports
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/types/*": ["./src/types/*"]
    }
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

Package Structure

The types package is organized into logical modules for different use cases.

index.ts

Type Categories

Utility Types

Common utility types for data manipulation and transformation.

import type {
  Optional,
  Required,
  Nullable,
  NonNullable,
  DeepPartial,
  DeepRequired,
  Prettify
} from '@foundrykit/types'

// Make specific properties optional
type UserWithOptionalFields = Optional<User, 'avatar' | 'bio'>

// Make specific properties required
type UserWithRequiredFields = Required<Partial<User>, 'id' | 'name'>

// Nullable and non-nullable types
type NullableString = Nullable<string>
type DefiniteString = NonNullable<string | null | undefined>

// Deep partial for nested objects
type PartialUserProfile = DeepPartial<{
  user: {
    id: string
    profile: {
      name: string
      settings: {
        theme: string
        notifications: boolean
      }
    }
  }
}>

// Prettify complex intersection types
type CleanType = Prettify<BaseProps & ExtendedProps & UtilityProps>

React Types

Comprehensive React component and hook types.

import type {
  ComponentProps,
  ComponentRef,
  ForwardRefComponent,
  PolymorphicComponent,
  WithChildren,
  WithClassName
} from '@foundrykit/types'

// Component props with common patterns
interface ButtonProps extends ComponentProps<'button'>, WithClassName {
  variant?: 'primary' | 'secondary' | 'danger'
  size?: 'sm' | 'md' | 'lg'
  loading?: boolean
}

// Forward ref component
const Button: ForwardRefComponent<'button', ButtonProps> = forwardRef(
  ({ className, variant = 'primary', size = 'md', loading, children, ...props }, ref) => {
    return (
      <button
        ref={ref}
        className={cn('btn', `btn-${variant}`, `btn-${size}`, className)}
        disabled={loading}
        {...props}
      >
        {loading ? 'Loading...' : children}
      </button>
    )
  }
)

// Polymorphic component (can render as different elements)
interface CardProps extends WithChildren, WithClassName {
  variant?: 'default' | 'elevated'
  padding?: 'none' | '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>
  )
}

Event Types

Type-safe event handling.

import type {
  EventHandler,
  ChangeHandler,
  SubmitHandler,
  KeyboardHandler,
  MouseHandler
} from '@foundrykit/types'

interface FormProps {
  onSubmit: SubmitHandler<FormData>
  onChange: ChangeHandler<string>
  onKeyDown: KeyboardHandler
  onClick: MouseHandler
}

export function SearchForm({ onSubmit, onChange, onKeyDown, onClick }: FormProps) {
  const handleSubmit: SubmitHandler<FormData> = (data, event) => {
    event.preventDefault()
    onSubmit(data, event)
  }

  const handleChange: ChangeHandler<string> = (value, event) => {
    onChange(value, event)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        onChange={(e) => handleChange(e.target.value, e)}
        onKeyDown={onKeyDown}
        onClick={onClick}
      />
      <button type="submit">Search</button>
    </form>
  )
}

API Types

Standardized API request and response types.

import type {
  ApiResponse,
  ApiError,
  PaginatedResponse,
  ApiEndpoint,
  HttpMethod
} from '@foundrykit/types'

// API response wrapper
interface UserApiResponse extends ApiResponse<User> {
  user: User
}

// Paginated API response
interface UsersListResponse extends PaginatedResponse<User> {
  users: User[]
}

// API error handling
interface CustomApiError extends ApiError {
  field?: string
  code: 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED'
}

// API endpoint configuration
interface UserEndpoints {
  getUser: ApiEndpoint<{ id: string }, UserApiResponse>
  getUsers: ApiEndpoint<{ page?: number; limit?: number }, UsersListResponse>
  createUser: ApiEndpoint<CreateUserRequest, UserApiResponse>
  updateUser: ApiEndpoint<{ id: string } & UpdateUserRequest, UserApiResponse>
  deleteUser: ApiEndpoint<{ id: string }, { success: boolean }>
}

// Usage in API client
class UserApiClient {
  async getUser(params: Parameters<UserEndpoints['getUser']>[0]) {
    const response = await fetch(`/api/users/${params.id}`)
    return response.json() as Promise<UserApiResponse>
  }

  async getUsers(params: Parameters<UserEndpoints['getUsers']>[0] = {}) {
    const searchParams = new URLSearchParams(
      Object.entries(params).map(([key, value]) => [key, String(value)])
    )
    const response = await fetch(`/api/users?${searchParams}`)
    return response.json() as Promise<UsersListResponse>
  }
}

Form Types

Comprehensive form handling types.

import type {
  FormData,
  FormField,
  ValidationResult,
  FormErrors,
  FieldValidator,
  FormState
} from '@foundrykit/types'

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

// Form data type
type UserFormData = FormData<UserFormFields>

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

// Custom validators
const nameValidator: FieldValidator<string> = (value) => {
  if (!value || value.length < 2) {
    return { isValid: false, error: 'Name must be at least 2 characters' }
  }
  return { isValid: true }
}

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 }
}

// Form state management
interface UserFormState extends FormState<UserFormData> {
  data: UserFormData
  errors: FormErrors<UserFormData>
  isSubmitting: boolean
  isValid: boolean
  isDirty: boolean
}

Type Guards and Predicates

Use type guards for runtime type checking.

import type { TypeGuard, Predicate } from '@foundrykit/types'
import { isString, isNumber, isObject, hasProperty } from '@foundrykit/types/guards'

// Built-in type guards
function processValue(value: unknown) {
  if (isString(value)) {
    // TypeScript knows value is string here
    return value.toUpperCase()
  }
  
  if (isNumber(value)) {
    // TypeScript knows value is number here
    return value.toFixed(2)
  }
  
  if (isObject(value) && hasProperty(value, 'name')) {
    // TypeScript knows value has a 'name' property
    return value.name
  }
  
  return 'Unknown value'
}

// Custom type guards
interface User {
  id: string
  name: string
  email: string
}

const isUser: TypeGuard<User> = (value): value is User => {
  return (
    isObject(value) &&
    hasProperty(value, 'id') && isString(value.id) &&
    hasProperty(value, 'name') && isString(value.name) &&
    hasProperty(value, 'email') && isString(value.email)
  )
}

// Usage with type narrowing
function handleUserData(data: unknown) {
  if (isUser(data)) {
    // TypeScript knows data is User here
    console.log(`Hello, ${data.name}!`)
    console.log(`Email: ${data.email}`)
  } else {
    console.error('Invalid user data')
  }
}

Integration Examples

React Component with Types

import React, { useState } from 'react'
import type {
  ComponentProps,
  WithChildren,
  WithClassName,
  EventHandler,
  ChangeHandler,
  Optional
} from '@foundrykit/types'

// Base component props
interface BaseCardProps extends WithChildren, WithClassName {
  title?: string
  variant?: 'default' | 'elevated' | 'outlined'
  padding?: 'none' | 'sm' | 'md' | 'lg'
}

// Interactive card props
interface InteractiveCardProps extends BaseCardProps {
  onClick?: EventHandler<MouseEvent>
  onHover?: EventHandler<MouseEvent>
  disabled?: boolean
}

// Form card props
interface FormCardProps extends BaseCardProps {
  onSubmit: SubmitHandler<FormData>
  loading?: boolean
}

// Generic card component
function Card<T extends BaseCardProps>({ 
  title,
  variant = 'default',
  padding = 'md',
  className,
  children,
  ...props
}: T) {
  return (
    <div
      className={cn(
        'card',
        `card-${variant}`,
        `card-padding-${padding}`,
        className
      )}
      {...props}
    >
      {title && <div className="card-header">{title}</div>}
      <div className="card-content">{children}</div>
    </div>
  )
}

// Usage examples
export function CardExamples() {
  return (
    <div className="space-y-4">
      {/* Basic card */}
      <Card title="Basic Card" variant="default">
        <p>This is a basic card.</p>
      </Card>

      {/* Interactive card */}
      <Card
        title="Interactive Card"
        variant="elevated"
        onClick={(event) => console.log('Card clicked:', event)}
        className="cursor-pointer"
      >
        <p>Click this card!</p>
      </Card>

      {/* Form card */}
      <Card
        title="Form Card"
        variant="outlined"
        onSubmit={(data, event) => {
          event.preventDefault()
          console.log('Form submitted:', data)
        }}
      >
        <form>
          <input type="text" placeholder="Enter text..." />
          <button type="submit">Submit</button>
        </form>
      </Card>
    </div>
  )
}

API Integration with Types

import type {
  ApiResponse,
  ApiError,
  PaginatedResponse,
  HttpMethod
} from '@foundrykit/types'

// API client with full type safety
class TypedApiClient {
  private baseUrl: string

  constructor(baseUrl: string) {
    this.baseUrl = baseUrl
  }

  private async request<TResponse>(
    method: HttpMethod,
    endpoint: string,
    data?: unknown
  ): Promise<TResponse> {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method,
      headers: {
        'Content-Type': 'application/json',
      },
      body: data ? JSON.stringify(data) : undefined,
    })

    if (!response.ok) {
      const error: ApiError = await response.json()
      throw new Error(error.message)
    }

    return response.json()
  }

  // Typed API methods
  async getUsers(params?: {
    page?: number
    limit?: number
    search?: string
  }): Promise<PaginatedResponse<User>> {
    const searchParams = new URLSearchParams()
    if (params?.page) searchParams.set('page', String(params.page))
    if (params?.limit) searchParams.set('limit', String(params.limit))
    if (params?.search) searchParams.set('search', params.search)

    const queryString = searchParams.toString()
    const endpoint = `/users${queryString ? `?${queryString}` : ''}`

    return this.request<PaginatedResponse<User>>('GET', endpoint)
  }

  async getUser(id: string): Promise<ApiResponse<User>> {
    return this.request<ApiResponse<User>>('GET', `/users/${id}`)
  }

  async createUser(userData: CreateUserRequest): Promise<ApiResponse<User>> {
    return this.request<ApiResponse<User>>('POST', '/users', userData)
  }

  async updateUser(
    id: string,
    userData: UpdateUserRequest
  ): Promise<ApiResponse<User>> {
    return this.request<ApiResponse<User>>('PUT', `/users/${id}`, userData)
  }

  async deleteUser(id: string): Promise<{ success: boolean }> {
    return this.request<{ success: boolean }>('DELETE', `/users/${id}`)
  }
}

// Usage with full type safety
const apiClient = new TypedApiClient('/api')

async function loadUserData() {
  try {
    // All parameters and return types are fully typed
    const usersResponse = await apiClient.getUsers({ 
      page: 1, 
      limit: 10, 
      search: 'john' 
    })
    
    console.log('Users:', usersResponse.data)
    console.log('Total:', usersResponse.pagination.total)
    
    if (usersResponse.data.length > 0) {
      const firstUser = usersResponse.data[0]
      const userDetails = await apiClient.getUser(firstUser.id)
      console.log('User details:', userDetails.data)
    }
  } catch (error) {
    console.error('Failed to load users:', error)
  }
}

Advanced Usage

Generic Type Utilities

import type { DeepPartial, DeepRequired, KeysOfType } from '@foundrykit/types'

// Extract keys of specific types
interface UserPreferences {
  theme: 'light' | 'dark'
  notifications: boolean
  language: string
  fontSize: number
}

type StringKeys = KeysOfType<UserPreferences, string> // 'theme' | 'language'
type BooleanKeys = KeysOfType<UserPreferences, boolean> // 'notifications'
type NumberKeys = KeysOfType<UserPreferences, number> // 'fontSize'

// Deep manipulation of nested types
interface NestedConfig {
  app: {
    name: string
    version: string
    features: {
      darkMode: boolean
      analytics: boolean
      notifications: {
        email: boolean
        push: boolean
        sms: boolean
      }
    }
  }
  user: {
    profile: {
      name: string
      email: string
    }
    preferences: UserPreferences
  }
}

// Make all properties optional deeply
type PartialConfig = DeepPartial<NestedConfig>

// Make all properties required deeply
type RequiredConfig = DeepRequired<PartialConfig>

TypeScript Integration

Strict Type Checking

Enable strict type checking for maximum type safety.

{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "useUnknownInCatchVariables": true,
    "noImplicitOverride": true
  }
}

Path Mapping

Set up path mapping for cleaner imports.

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/types": ["@foundrykit/types"],
      "@/components/*": ["./src/components/*"],
      "@/utils/*": ["./src/utils/*"]
    }
  }
}

Next Steps