Strategy Intermediate

TypeScript 5 Trading System Type Safety Practice

Sentinel Team · 2026-03-09
TypeScript 5 Trading System Type Safety Practice

TypeScript 5 Trading System Type Safety Practice

Quick Navigation: This article deeply explores TypeScript 5 applications in trading systems, from basic types to advanced techniques, providing a complete guide for creating type-safe, maintainable trading applications. Estimated reading time: 13 minutes.


Why Do Trading Systems Need TypeScript?

In the financial trading field, one type error can lead to millions in losses. Imagine this scenario:

// ❌ JavaScript: Silent error
const order = {
  symbol: 'BTC/USDT',
  side: 'buy',
  quantity: '0.5', // String! Should be number
  price: 50000
};

// Error when calculating total
const total = order.quantity * order.price; // NaN

TypeScript 5 provides compile-time type checking, catching such errors before code runs. According to GitHub research, TypeScript can reduce runtime errors by 15%.

TypeScript 5 Core Advantages

| Advantage | Description | Trading System Value |

|:---|:---|:---|

| Compile-time Checking | Errors caught at compile time | Avoid production crashes |

| IntelliSense | IDE auto-completion and docs | Improve dev efficiency 30% |

| Safe Refactoring | Rename, extract functions safely | Large-scale refactoring without fear |

| Self-documenting Code | Types are documentation | Reduce maintenance cost |

| Team Collaboration | Interfaces are contracts | Reduce communication cost |


TypeScript 5 New Features

Decorators

// Trade operation log decorator
function logTrade(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  
  descriptor.value = async function (...args: any[]) {
    console.log(`[TRADE] ${propertyKey} called with:`, args);
    const result = await originalMethod.apply(this, args);
    console.log(`[TRADE] ${propertyKey} result:`, result);
    return result;
  };
}

class TradingService {
  @logTrade
  async placeOrder(order: OrderRequest): Promise<OrderResponse> {
    // Implementation
  }
}

const Type Parameters

// TypeScript 5 new syntax: More precise type inference
function createTradePair<const T extends readonly string[]>(symbols: T): T {
  return symbols;
}

// Type inferred as readonly ["BTC", "ETH"] rather than string[]
const pairs = createTradePair(["BTC", "ETH"] as const);

Better Type Inference

// TypeScript 5 improved generic inference
function createBotConfig<T extends BotStrategy>(config: T) {
  return config;
}

// Automatically infers complete type, no explicit specification needed
const config = createBotConfig({
  strategy: 'EMA_CROSS',
  params: { fast: 10, slow: 20 },
  risk: { maxPosition: 0.1 }
});

Trading System Type Design

Core Domain Types

// types/trading.ts

// Basic types
export type Symbol = string & { __brand: 'Symbol' };
export type OrderId = string & { __brand: 'OrderId' };
export type BotId = string & { __brand: 'BotId' };

// Trading pair
export interface TradingPair {
  symbol: Symbol;
  baseAsset: string;
  quoteAsset: string;
  pricePrecision: number;
  quantityPrecision: number;
  minQuantity: number;
  maxQuantity: number;
}

// Order side
export type OrderSide = 'BUY' | 'SELL';

// Order type
export type OrderType = 
  | 'MARKET'      // Market order
  | 'LIMIT'       // Limit order
  | 'STOP_LOSS'   // Stop loss order
  | 'TAKE_PROFIT'; // Take profit order

// Order status
export type OrderStatus = 
  | 'PENDING'     // Pending
  | 'OPEN'        // Open
  | 'PARTIALLY_FILLED' // Partially filled
  | 'FILLED'      // Filled
  | 'CANCELLED'   // Cancelled
  | 'REJECTED';   // Rejected

// Order request
export interface OrderRequest {
  symbol: Symbol;
  side: OrderSide;
  type: OrderType;
  quantity: number;
  price?: number;           // Required for limit orders
  stopPrice?: number;       // Required for stop/take profit orders
  timeInForce?: 'GTC' | 'IOC' | 'FOK';
  clientOrderId?: string;
}

// Order response
export interface OrderResponse {
  orderId: OrderId;
  clientOrderId?: string;
  symbol: Symbol;
  status: OrderStatus;
  side: OrderSide;
  type: OrderType;
  price: number;
  quantity: number;
  executedQuantity: number;
  createdAt: Date;
  updatedAt: Date;
}

Advanced Generic Applications

// types/utils.ts

// Nullable types
export type Nullable<T> = T | null;
export type Optional<T> = T | undefined;

// API response wrapper
export interface ApiResponse<T> {
  success: boolean;
  data: T;
  message?: string;
  timestamp: number;
}

export interface ApiError {
  code: string;
  message: string;
  details?: Record<string, unknown>;
}

// Paginated response
export interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
}

// Event type
export type EventPayload<E extends { type: string; payload: unknown }> = E['payload'];

// Trade event
export interface TradeEvent {
  type: 'TRADE_EXECUTED' | 'ORDER_FILLED' | 'POSITION_UPDATED';
  payload: {
    tradeId: string;
    symbol: Symbol;
    side: OrderSide;
    price: number;
    quantity: number;
    timestamp: Date;
  };
}

React + TypeScript Best Practices

Component Props Types

// components/OrderButton.tsx
import { ButtonHTMLAttributes } from 'react';

interface OrderButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  side: 'BUY' | 'SELL';
  size?: 'sm' | 'md' | 'lg';
  isLoading?: boolean;
  onOrderSubmit: (side: OrderSide) => void;
}

export function OrderButton({
  side,
  size = 'md',
  isLoading,
  onOrderSubmit,
  children,
  ...props
}: OrderButtonProps) {
  const variant = side === 'BUY' ? 'success' : 'danger';
  
  return (
    <button
      type="button"
      data-side={side}
      data-size={size}
      disabled={isLoading}
      onClick={() => onOrderSubmit(side)}
      {...props}
    >
      {isLoading ? 'Processing...' : children}
    </button>
  );
}

Custom Hooks Types

// hooks/useBot.ts
import { useQuery, UseQueryOptions } from '@tanstack/react-query';

interface UseBotOptions extends Omit<
  UseQueryOptions<Bot, Error>,
  'queryKey' | 'queryFn'
> {
  refetchInterval?: number;
}

export function useBot(id: BotId, options?: UseBotOptions) {
  return useQuery<Bot, Error>({
    queryKey: ['bots', id],
    queryFn: () => api.bots.get(id),
    enabled: !!id,
    ...options,
  });
}

// Usage
const { data: bot, error, isLoading } = useBot('bot-123', {
  refetchInterval: 5000,
});

Context Types

// contexts/TradingContext.tsx
import { createContext, useContext } from 'react';

interface TradingContextValue {
  currentSymbol: Symbol;
  setCurrentSymbol: (symbol: Symbol) => void;
  tickSize: number;
  lotSize: number;
}

const TradingContext = createContext<TradingContextValue | null>(null);

export function useTrading() {
  const context = useContext(TradingContext);
  if (!context) {
    throw new Error('useTrading must be used within TradingProvider');
  }
  return context;
}

Zod Runtime Validation

Why Need Zod?

TypeScript only checks at compile time, runtime data still needs validation. Zod provides TypeScript-first schema validation:

// schemas/order.ts
import { z } from 'zod';

// Define schema
export const orderRequestSchema = z.object({
  symbol: z.string().min(1),
  side: z.enum(['BUY', 'SELL']),
  type: z.enum(['MARKET', 'LIMIT', 'STOP_LOSS', 'TAKE_PROFIT']),
  quantity: z.number().positive(),
  price: z.number().positive().optional(),
  stopPrice: z.number().positive().optional(),
  timeInForce: z.enum(['GTC', 'IOC', 'FOK']).default('GTC'),
});

// Derive type
export type OrderRequest = z.infer<typeof orderRequestSchema>;

// Runtime validation
function validateOrder(data: unknown): OrderRequest {
  return orderRequestSchema.parse(data);
}

// Safe parsing (no throw)
function safeValidateOrder(data: unknown) {
  return orderRequestSchema.safeParse(data);
}

API Response Validation

// schemas/api.ts
export const apiResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) =>
  z.object({
    success: z.boolean(),
    data: dataSchema,
    message: z.string().optional(),
    timestamp: z.number(),
  });

// Usage
const botResponseSchema = apiResponseSchema(botSchema);

async function fetchBot(id: string): Promise<Bot> {
  const response = await api.get(`/bots/${id}`);
  const parsed = botResponseSchema.parse(response);
  return parsed.data;
}

Type-Safe Error Handling

Result Type Pattern

// types/result.ts
export type Result<T, E = Error> = 
  | { success: true; data: T }
  | { success: false; error: E };

// Usage
async function placeOrder(request: OrderRequest): Promise<Result<OrderResponse>> {
  try {
    const response = await api.orders.place(request);
    return { success: true, data: response };
  } catch (error) {
    return { 
      success: false, 
      error: error instanceof Error ? error : new Error('Unknown error')
    };
  }
}

// Caller handling
const result = await placeOrder(orderRequest);

if (result.success) {
  console.log('Order placed:', result.data.orderId);
} else {
  console.error('Order failed:', result.error.message);
}

Real-world Case: Sentinel Bot Type System

Project Type Structure

src/
├── types/
│   ├── index.ts          # Public exports
│   ├── trading.ts        # Trading domain
│   ├── api.ts            # API types
│   ├── websocket.ts      # WebSocket types
│   └── utils.ts          # Utility types
├── schemas/
│   ├── index.ts
│   ├── order.ts          # Order validation
│   ├── bot.ts            # Bot validation
│   └── api.ts            # API response validation
└── components/
    └── **/*.types.ts     # Component-specific types

Type Coverage

| Module | Type Coverage | Notes |

|:---|:---:|:---|

| API Layer | 100% | All APIs have type definitions |

| Components | 98% | Props + State complete types |

| Store | 100% | Zustand + TypeScript |

| Utility Functions | 95% | Generic functions complete types |


Frequently Asked Questions FAQ

Q1: Is TypeScript learning curve steep?

A: Progressive learning:

  1. Start with any (allowed but warned)
  2. Gradually add basic types
  3. Learn interfaces and type aliases
  4. Master generics and advanced techniques

Q2: How to handle third-party libraries without types?

A: Declare modules:

// types/library.d.ts
declare module 'some-library' {
  export function doSomething(): void;
}

Or install @types/ packages.

Q3: Type checking too slow what to do?

A: Optimization strategies:

Q4: Can any type be used?

A: Try to avoid, but acceptable in these cases:

Q5: When to use generics?

A: When functions/components need to handle multiple types:

// Generic data fetching hook
function useData<T>(fetcher: () => Promise<T>) {
  const [data, setData] = useState<T | null>(null);
  // ...
}

Q6: How to combine types with runtime validation?

A: TypeScript + Zod golden combination:

Q7: How to test types?

A: Use tsd or expect-type:

import { expectType } from 'tsd';

expectType<string>(order.symbol);

Q8: Should strict mode be enabled?

A: Strongly recommended:

{
  "compilerOptions": {
    "strict": true
  }
}

May have many errors initially, but huge long-term benefits.


Conclusion and Action Recommendations

TypeScript 5 brings to trading systems:

Take Action Now


Extended Reading:


Author: Sentinel Team

Last Updated: 2026-03-04

Technical Verification: Based on Sentinel Bot production environment practical experience


Improving your trading system code quality? Experience Sentinel Bot's TypeScript 5 driven architecture now, or download our type templates to get started quickly.

Free Trial Sentinel Bot | Download Type Templates | Technical Consultation


Related Articles

Same Series Extended Reading

Cross-series Recommendations