Skip to main content

TypeScript Standards

Deltalytix is built with TypeScript in strict mode. All code must be properly typed.

TypeScript Configuration

The project uses these TypeScript settings:
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "strict": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "paths": {
      "@/*": ["./*"]
    }
  }
}

Type Safety

// Good
function calculatePnL(trades: Trade[]): number {
  return trades.reduce((sum, trade) => sum + trade.pnl, 0)
}

// Bad
function calculatePnL(trades) {
  return trades.reduce((sum, trade) => sum + trade.pnl, 0)
}
// Good
interface TradeData {
  instrument: string
  pnl: number
  quantity: number
}
function processData(data: TradeData): void { }

// Bad
function processData(data: any): void { }
// Good
type TradeSide = 'long' | 'short'
type TradeStatus = 'open' | 'closed' | 'pending'

interface Trade {
  side: TradeSide
  status: TradeStatus
}
// Good - type is inferred
const trades = await prisma.trade.findMany()
const total = trades.length

// Unnecessary - already inferred
const trades: Trade[] = await prisma.trade.findMany()
const total: number = trades.length

React Best Practices

Component Structure

Example Component
'use client' // Only if client component

import { useState } from 'react'
import { useI18n } from '@/locales/client'

interface TradeCardProps {
  trade: Trade
  onUpdate?: (trade: Trade) => void
}

export function TradeCard({ trade, onUpdate }: TradeCardProps) {
  const t = useI18n()
  const [isEditing, setIsEditing] = useState(false)

  return (
    <div className="rounded-lg border p-4">
      <h3>{t('trade-table.instrument')}</h3>
      {/* Component content */}
    </div>
  )
}

Component Guidelines

1

Use Functional Components

Always use functional components with hooks, not class components.
2

Server vs Client Components

Default to Server Components. Only use 'use client' when needed for:
  • Event handlers
  • Browser APIs
  • State management
  • React hooks
3

Props Interface

Define explicit interfaces for component props.
4

Destructure Props

Destructure props in the function signature for clarity.

Hooks Usage

// Good - hooks at top level
function TradeAnalysis() {
  const t = useI18n()
  const [trades, setTrades] = useState<Trade[]>([])
  const filteredTrades = useMemo(
    () => trades.filter(t => t.pnl > 0),
    [trades]
  )

  return <div>{/* content */}</div>
}

// Bad - conditional hooks
function TradeAnalysis() {
  if (condition) {
    const t = useI18n() // ❌ Never
  }
  return <div>{/* content */}</div>
}

File Organization

Project Structure

deltalytix/
├── app/                    # Next.js App Router
│   ├── [locale]/          # Internationalized routes
│   │   ├── dashboard/     # Dashboard pages
│   │   └── (landing)/     # Marketing pages
│   └── api/               # API routes
├── components/            # React components
│   ├── ui/               # Base UI components
│   └── [feature]/        # Feature-specific components
├── lib/                  # Utility functions
├── hooks/                # Custom React hooks
├── store/                # Zustand stores
├── server/               # Server-side logic
├── locales/              # Translations (EN/FR)
└── prisma/               # Database schema

File Naming Conventions

Components

TradeCard.tsx
TradeTable.tsx
PnLChart.tsx
PascalCase for components

Utilities

formatCurrency.ts
calculatePnL.ts
parseTradeData.ts
camelCase for utilities

Hooks

useTradeFilters.ts
useTrades.ts
useEquityChart.ts
use + PascalCase prefix

Types

types.ts
trade.types.ts
api.types.ts
.types.ts suffix

Naming Conventions

Variables and Functions

// Variables - camelCase
const tradeData = []
const totalPnl = 0
const isLoading = false

// Functions - camelCase, descriptive verbs
function calculateTotalPnl(trades: Trade[]): number { }
function formatTradeDate(date: Date): string { }
function filterProfitableTrades(trades: Trade[]): Trade[] { }

// Boolean variables - is/has/can prefix
const isAuthenticated = true
const hasPermission = false
const canEdit = true

// Constants - UPPER_SNAKE_CASE
const MAX_TRADES_PER_PAGE = 50
const API_BASE_URL = 'https://api.example.com'
const DEFAULT_COMMISSION_RATE = 2.5

Interfaces and Types

// Interfaces - PascalCase, descriptive nouns
interface Trade {
  id: string
  instrument: string
  pnl: number
  side: 'long' | 'short'
}

interface TradeFilters {
  dateRange?: DateRange
  instruments?: string[]
  minPnl?: number
}

// Type aliases - PascalCase
type TradeSide = 'long' | 'short'
type DateRange = { from: Date; to: Date }

// Generic types - single capital letter
type ApiResponse<T> = {
  data: T
  error?: string
}

Code Formatting

ESLint Configuration

The project uses ESLint with Next.js configuration:
eslint.config.mjs
import { defineConfig, globalIgnores } from "eslint/config"
import nextVitals from "eslint-config-next/core-web-vitals"
import nextTs from "eslint-config-next/typescript"

const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  globalIgnores([".next/**", "out/**", "build/**"])
])

export default eslintConfig

Formatting Rules

  • Use 2 spaces for indentation
  • No tabs
  • Consistent across all files

Import Organization

// 1. External dependencies
import { useState, useEffect } from 'react'
import { toast } from 'sonner'

// 2. Internal dependencies with @ alias
import { Button } from '@/components/ui/button'
import { useI18n } from '@/locales/client'
import { formatCurrency } from '@/lib/utils'

// 3. Relative imports
import { TradeCard } from './TradeCard'
import type { Trade } from './types'

// 4. Type-only imports at the end
import type { ReactNode } from 'react'

Best Practices

Error Handling

// Good - Proper error handling
try {
  const trades = await fetchTrades()
  return trades
} catch (error) {
  console.error('Failed to fetch trades:', error)
  toast.error(t('error'), {
    description: t('dashboard.loadingError')
  })
  return []
}

// Bad - Silent failures
try {
  const trades = await fetchTrades()
  return trades
} catch (error) {
  // Silent failure
}

Self-Documenting Code

// Good - Clear and self-explanatory
function calculateAverageWinRate(trades: Trade[]): number {
  const winningTrades = trades.filter(trade => trade.pnl > 0)
  return (winningTrades.length / trades.length) * 100
}

// Bad - Unclear variable names
function calc(t: any[]): number {
  const w = t.filter(x => x.p > 0)
  return (w.length / t.length) * 100
}

Comments

When to Comment

  • Complex business logic
  • Non-obvious solutions
  • Workarounds and hacks
  • Important decisions
  • TODOs with context

When NOT to Comment

  • Obvious code
  • Redundant descriptions
  • Commented-out code
  • Version history (use git)
// Good - Explains WHY
// Rithmic API returns times in UTC but without timezone indicator
// We need to manually convert to user's timezone
const localTime = convertRithmicTimeToLocal(trade.timestamp)

// Bad - Explains WHAT (obvious)
// Loop through trades
for (const trade of trades) {
  // Calculate PnL
  const pnl = calculatePnL(trade)
}

State Management

// Client-side state - Zustand stores
import { create } from 'zustand'

interface TradeStore {
  trades: Trade[]
  setTrades: (trades: Trade[]) => void
  addTrade: (trade: Trade) => void
}

export const useTradeStore = create<TradeStore>((set) => ({
  trades: [],
  setTrades: (trades) => set({ trades }),
  addTrade: (trade) => set((state) => ({
    trades: [...state.trades, trade]
  }))
}))

// Server-side - Server Actions
export async function updateTrade(tradeId: string, data: TradeUpdate) {
  'use server'
  
  const user = await getUser()
  if (!user) throw new Error('Unauthorized')
  
  return prisma.trade.update({
    where: { id: tradeId },
    data
  })
}

API Design

Use for mutations and authenticated operations:
'use server'

export async function deleteTrade(tradeId: string) {
  const user = await getUser()
  if (!user) throw new Error('Unauthorized')
  
  return prisma.trade.delete({
    where: { id: tradeId, userId: user.id }
  })
}

Performance Considerations

Memoization

import { useMemo, useCallback } from 'react'

function TradeAnalysis({ trades }: { trades: Trade[] }) {
  // Memoize expensive calculations
  const statistics = useMemo(() => {
    return calculateComplexStatistics(trades)
  }, [trades])
  
  // Memoize callbacks passed to children
  const handleTradeUpdate = useCallback((trade: Trade) => {
    updateTrade(trade)
  }, [])
  
  return <div>{/* content */}</div>
}

Lazy Loading

import dynamic from 'next/dynamic'

// Lazy load heavy components
const AdvancedChart = dynamic(
  () => import('@/components/charts/AdvancedChart'),
  { loading: () => <ChartSkeleton /> }
)

Testing Guidelines

While Deltalytix doesn’t have a comprehensive test suite yet, follow these guidelines:

Manual Testing Checklist

  • Test on desktop browsers (Chrome, Firefox, Safari)
  • Test on mobile devices (iOS, Android)
  • Test with different data sets (empty, small, large)
  • Test error scenarios
  • Test different user permissions
  • Test with different locales (EN, FR)

Future Testing

We plan to add:
  • Unit tests with Jest
  • Component tests with React Testing Library
  • E2E tests with Playwright
Contributions that add tests are highly appreciated!

Resources

Build docs developers (and LLMs) love