Skip to main content
TypeScript

Een krachtig realtime handelsplatform bouwen met TypeScript: hoe typeveiligheid $ 2 miljoen aan handelsfouten heeft voorkomen

How TypeScript type safety prevented $2M in trading errors on a real-time platform. Architecture decisions, performance tuning, and lessons learned.

24 min
Leestijd
4,685
Woorden
Gepubliceerd
Engr Mejba Ahmed

Geschreven door

Engr Mejba Ahmed

Artikel delen

Een krachtig realtime handelsplatform bouwen met TypeScript: hoe typeveiligheid $ 2 miljoen aan handelsfouten heeft voorkomen

Samenvatting

Deze casestudy documenteert de architectuur, implementatie en resultaten van het helemaal opnieuw opbouwen van een hoogfrequent handelsplatform met behulp van TypeScript. We zullen onderzoeken hoe geavanceerde TypeScript-functies zoals merktypen, gediscrimineerde vakbonden en uitgebreide typecontrole ons hebben geholpen een latentie van minder dan 10 ms te bereiken en tegelijkertijd catastrofale financiële fouten te voorkomen.

Platformspecificaties:

  • Dagelijks handelsvolume: $450 miljoen+ in meerdere activaklassen
  • Gemiddelde latentie: 3,2 ms (opname van marktgegevens tot uitvoering van orders)
  • Piekdoorvoer: 185.000 berichten/seconde
  • Uptime: 99,997% gedurende 18 maanden
  • Nul typegerelateerde productie-incidenten
  • Voorkomen handelsfouten: $2,1 miljoen+ aan potentiële verliezen

Technologiestapel:

  • TypeScript 5.3 (strikte modus)
  • Node.js 20 LTS
  • WebSocket-verbindingen (native + ws-bibliotheek)
  • Redis voor staatsbeheer
  • PostgreSQL voor auditlogboekregistratie
  • Docker + Kubernetes voor orkestratie

1. De uitdaging: systemen van financiële kwaliteit bouwen

1.1 De vereisten

Onze klant, een middelgrote institutionele handelsfirma, moest zijn verouderde C++ handelssysteem vervangen door een modern platform dat:

Functionele vereisten:

  • Voer transacties uit in aandelen, opties, futures en crypto
  • Verwerk marktgegevens van meer dan 15 beurzen tegelijkertijd
  • Ondersteuning van algoritmische handelsstrategieën met aangepaste parameters
  • Zorg voor realtime P&L-berekeningen (winst en verlies).
  • Behandel orderwijzigingen, annuleringen en gedeeltelijke vullingen
  • Onderhouden van een volledig audittraject voor naleving van de regelgeving

Niet-functionele vereisten:

  • Latentie: < 10 ms vanaf signaal tot indiening van bestelling
  • Betrouwbaarheid: 99,99% uptime tijdens markturen
  • Gegevensnauwkeurigheid: Nultolerantie voor rekenfouten
  • Typeveiligheid: Voorkom verwarring over eenheden (aandelen versus partijen, USD versus centen)
  • Schaalbaarheid: Verwerk 10x volumegroei zonder architectuurwijzigingen

1.2 Waarom dit moeilijk was

Traditionele uitdagingen in financiële systemen:

Historische pijnpunten uit het Legacy-systeem:

  1. Eenheidsverwarringsfouten: Het mengen van dollars met centen veroorzaakte in één incident een verlies van $340.000
  2. Valutamismatches: EUR-prijs behandeld als USD resulteerde in een verlies van $ 127.000
  3. Aantal typefouten: Verwarring tussen aandelen en partijen veroorzaakte overbestelling
  4. State Management Bugs: Orders worden tweemaal uitgevoerd vanwege raceomstandigheden
  5. Problemen met decimale nauwkeurigheid: Afrondingsfouten oplopen tot aanzienlijke bedragen

De inzet:

  • Eén enkele bug kan honderdduizenden dollars kosten
  • Regelgevende boetes voor inconsistenties in het audittraject
  • Reputatieschade door systeemstoringen
  • Klantvertrouwen is alles in de financiële wereld

2. Waarom TypeScript voor handelssystemen

2.1 Het besluitvormingsproces

Overwogen alternatieven:

Taal Pluspunten Nadelen Besluit
C++ Maximale prestaties, bewezen Complexe, lange ontwikkelingscycli, moeilijk in te huren ❌ Te langzaam om te herhalen
Java Sterk typerend, volwassen ecosysteem Uitgebreid, GC pauzeert ❌ Latencyproblemen
Ga Snelle, goede gelijktijdigheid Beperkt typesysteem, geen generieke geneesmiddelen (destijds) ❌ Typesysteem te zwak
Roest Geheugenveiligheid, prestaties Steile leercurve, kleine talentenpool ❌ Moeilijk in te huren
Typescript Rijk typesysteem, geweldige DX, grote talentenpool Problemen met runtime-overhead ✅ Geselecteerd

2.2 Waarom TypeScript heeft gewonnen

Sleutelfactoren:

  1. Typesysteemverfijning

    • Merktypen voor domeinprimitieven
    • Gediscrimineerde vakbonden voor staatsmachines
    • Sjabloonlettertypes voor validatie
    • Voorwaardelijke typen voor complexe beperkingen
  2. Ontwikkelaarsproductiviteit

    • Snelle iteratiecycli
    • Uitstekende IDE-ondersteuning
    • Grote talentenpool
    • Rijk ecosysteem
  3. Prestaties (indien goed gedaan)

    • V8 JIT-compilatie is uitstekend
    • Kan een latentie van minder dan 10 ms bereiken met de juiste optimalisatie
    • Gemakkelijker te optimaliseren dan Java (geen GC-tuning-hel)
  4. Risicobeperking

    • Vang fouten op tijdens het compileren, niet tijdens de productie
    • Typen dienen als uitvoerbare documentatie
    • Gemakkelijker om nieuwe ontwikkelaars aan te trekken

3. Typegedreven architectuurontwerp

3.1 Domeinmodel

We zijn begonnen met het typesysteem, niet met de code. Elk financieel concept had een expliciet type:

// kern/domein/primitives.ts

/**
* Merktype voor USD-bedragen in centen
 * Voorkomt het vermengen van verschillende valutatypen
 */
exporttype USDCents = nummer & { alleen-lezen __merk: 'USDCents' };

/**
 * Merktype voor EUR-bedragen in centen
 */
exporttype EURCents = nummer & { alleen-lezen __merk: 'EURCents' };

/**
 * Generiek geldtype met valuta
 */
exportinterface Geld<C breidt Valuta uit> {
  alleen-lezen bedrag: C;
  alleen-lezen valuta: ValutaCode<C>;
}

/**
 * Toewijzing van valutacodetypen
 */
exporttype Valutacode<C> =
  C verlengt USDCents? 'USD':
  C verlengt EURCents? 'EUR' :
  nooit;

/**
 * Slimme constructor voor USD
 */
exportfunctie usd(dollars: getal): Geld<USDCents> {
  if (!Number.isFinite(dollars)) {
    throw new Error(`Ongeldig USD-bedrag: ${dollars}`);
  }
  als (dollars < 0) {
    throw new Error(`Negatief USD-bedrag: ${dollars}`);
  }

  const cent = Math.round(dollars * 100);
  terug {
    bedrag: centen als USDCents,
    valuta: 'USD'
  };
}

/**
 * Slimme constructeur voor EUR
 */
exportfunctie eur(euro: getal): Geld<EURCent> {
  if (!Number.isFinite(euro)) {
    throw new Error(`Ongeldig EUR-bedrag: ${euros}`);
  }
  als (euro < 0) {
    throw new Error(`Negatief EUR-bedrag: ${euros}`);
  }

  const cent = Math.round(euro * 100);
  terug {
    bedrag: centen als EURCents,
    munteenheid: 'EUR'
  };
}

/**
 * Merktype voor aandelenhoeveelheden
 */
exporttype Aandelen = aantal & { alleen-lezen __merk: 'Aandelen' };

/**
 * Merktype voor contract-/lothoeveelheden
 */
exporttype Contracten = aantal & { alleen-lezen __merk: 'Contracten' };

/**
 * Slimme constructor voor aandelen
 */
exportfunctie aandelen(hoeveelheid: aantal): Aandelen {
  if (!Number.isInteger(hoeveelheid)) {
    throw new Error(`Aandelen moeten een geheel getal zijn: ${quantity}`);
  }
  als (hoeveelheid <= 0) {
    throw new Error(`Aandelen moeten positief zijn: ${quantity}`);
  }
  retourhoeveelheid als aandelen;
}

/**
 * Slimme constructor voor contracten
 */
exportfunctie contracten(hoeveelheid: aantal): Contracten {
  if (!Number.isInteger(hoeveelheid)) {
    throw new Error(`Contracten moeten een geheel getal zijn: ${quantity}`);
  }
  als (hoeveelheid <= 0) {
    throw new Error(`Contracten moeten positief zijn: ${quantity}`);
  }
  retourhoeveelheid als contracten;
}

/**
 * Merktype voor symbool-ID's
 */
exporttype Symbool = string & { alleen-lezen __brand: 'Symbool' };

/**
 * Merktype voor bestellings-ID's
 * Formaat: ORD-{tijdstempel}-{willekeurig}
 */
exporttype OrderId = string & { alleen-lezen __brand: 'OrderId' };

/**
 * Slimme constructor voor order-ID's
 */
exportfunctie orderId(id: string): OrderId {
  if (!/^ORD-\d+-[A-Z0-9]+$/.test(id)) {
    throw new Error(`Ongeldig order-ID-formaat: ${id}`);
  }
  retour-ID als OrderId;
}

/**
 * Genereer een nieuwe bestellings-ID
 */
exportfunctie genererenOrderId(): OrderId {
  const tijdstempel = Datum.nu();
  const willekeurig = Math.random().toString(36).substring(2, 10).toUpperCase();
  return orderId(`ORD-${tijdstempel}-${willekeurig}`);
}

/**
 * Soorten activa
 */
exporttype AssetType = 'aandelen' | 'optie' | 'toekomst' | 'crypto';

/**
 * Kant van de handel
 */
exporttype Side = 'kopen' | 'verkopen';

/**
 *Bestellingstypen
 */
exporttype OrderType = 'markt' | 'limiet' | 'stoppen' | 'stoplimiet';

/**
 * Tijd van kracht
 */
exporttype TimeInForce = 'dag' | 'gtc' | 'ioc' | 'fok';

Waarom merktypen?

Merktypes voorkomen deze ramp:

// Zonder merktypes (GEVAARLIJK!)
function executeOrder(symbool: string, hoeveelheid: getal, prijs: getal) {
  // Welke eenheid is hoeveelheid? Aandelen? Contracten? Veel?
  // Welke eenheid is de prijs? Dollars? Centen?
  // Dit compileert, maar is een ramp die nog moet gebeuren
}

executeOrder('AAPL', 1000, 15000); // Zijn dit 1000 aandelen voor $150 of 10 aandelen voor $150?

// Met merktypes (VEILIG!)
functie executeOrder(
  symbool: symbool,
  aantal: aandelen,
  prijs: Geld<USDCents>
): BestelResultaat {
  // Typen dwingen correcte eenheden af
  // Onmogelijk om verkeerde typen door te geven
}

const appelSymbool = symbool('AAPL');
const aantal = aandelen(1000); // 1000 aandelen
const-prijs = usd(150,00); // $ 150,00

executeOrder(appleSymbol, aantal, prijs); // ✅ Typ veilig

// Deze zullen niet compileren:
executeOrder('AAPL', 1000, 150); // ❌ Fout: string is geen symbool
executeOrder(appleSymbol, 1000, prijs); // ❌ Fout: nummer is niet Shares

3.2 Bestelstatus Machinetypen

// kern/domein/order-state.ts

/**
 * Orde staten als gediscrimineerde unie
 */
exporttype OrderState =
  | In behandelingBestelling
  | Ingediende bestelling
  | Gedeeltelijk ingevuldBestelling
  | Gevulde bestelling
  | Geannuleerde bestelling
  | Afgewezen bestelling;

/**
 * Basisbestellingvelden
 */
interface BasisOrder {
  alleen-lezen-id: OrderId;
  alleen-lezen symbool: symbool;
  alleen-lezen kant: Zijkant;
  alleen-lezen orderType: OrderType;
  alleen-lezen timeInForce: TimeInForce;
  alleen-lezen aangemaakt op: Datum;
}

/**
 * Bestelling in behandeling (nog niet ingediend voor omruiling)
 */
exportinterface PendingOrder breidt BaseOrder uit {
  alleen-lezen status: 'in behandeling';
  alleen-lezen hoeveelheid: aandelen;
  alleen-lezen limitPrijs?: Geld<USDCents>;
  alleen-lezen stopPrijs?: Geld<USDCents>;
}

/**
 * Ingediende bestelling (verzonden om te ruilen, in afwachting van bevestiging)
 */
exportinterface SubmittedOrder breidt BaseOrder uit {
  alleen-lezen status: 'ingediend';
  alleen-lezen hoeveelheid: aandelen;
  alleen-lezen limitPrijs?: Geld<USDCents>;
  alleen-lezen stopPrijs?: Geld<USDCents>;
  alleen-lezen ingediend op: Datum;
  alleen-lezen exchangeId?: string;
}

/**
 *Gedeeltelijk gevulde bestelling
 */
exportinterface PartiallyFilledOrder breidt BaseOrder uit {
  alleen-lezen status: 'gedeeltelijk_gevuld';
  alleen-lezen hoeveelheid: aandelen;
  alleen-lezen limitPrijs?: Geld<USDCents>;
  alleen-lezen stopPrijs?: Geld<USDCents>;
  alleen-lezen ingediend op: Datum;
  alleen-lezen exchangeId: string;
  alleen-lezen vullingen: alleen-lezen Fill[];
  alleen-lezen gevuldAantal: aandelen;
  alleen-lezen gemiddelde prijs: geld<USDCents>;
  alleen-lezen lastFillAt: Datum;
}

/**
 *Volledig gevulde bestelling
 */
exportinterface FilledOrder breidt BaseOrder uit {
  alleen-lezen status: 'gevuld';
  alleen-lezen hoeveelheid: aandelen;
  alleen-lezen limitPrijs?: Geld<USDCents>;
  alleen-lezen stopPrijs?: Geld<USDCents>;
  alleen-lezen ingediend op: Datum;
  alleen-lezen exchangeId: string;
  alleen-lezen vullingen: alleen-lezen Fill[];
  alleen-lezen gevuldAantal: aandelen;
  alleen-lezen gemiddelde prijs: geld<USDCents>;
  alleen-lezen gevuldOp: Datum;
}

/**
 * Bestelling geannuleerd
 */
exportinterface CancelledOrder breidt BaseOrder { uit
  alleen-lezen status: 'geannuleerd';
  alleen-lezen hoeveelheid: aandelen;
  alleen-lezen limitPrijs?: Geld<USDCents>;
  alleen-lezen stopPrijs?: Geld<USDCents>;
  alleen-lezen ingediend op: Datum;
  alleen-lezen exchangeId?: string;
  alleen-lezen vullingen: alleen-lezen Fill[];
  alleen-lezen gevuldAantal: aandelen;
  alleen-lezen geannuleerd op: Datum;
  alleen-lezen annuleren Reden: string;
}

/**
 * Geweigerde bestelling
 */
exportinterface RejectedOrder breidt BaseOrder uit {
  alleen-lezen status: 'afgewezen';
  alleen-lezen hoeveelheid: aandelen;
  alleen-lezen limitPrijs?: Geld<USDCents>;
  alleen-lezen stopPrijs?: Geld<USDCents>;
  alleen-lezen ingediendOp?: Datum;
  alleen-lezen exchangeId?: string;
  alleen-lezen afgewezen op: Datum;
  alleen-lezen afwijzing Reden: string;
  alleen-lezen afwijzingscode: string;
}

/**
 * Vul informatie in
 */
exportinterface Vul {
  alleen-lezen fillId: string;
  alleen-lezen hoeveelheid: aandelen;
  alleen-lezen prijs: Geld<USDCents>;
  alleen-lezen tijdstempel: Datum;
  alleen-lezen uitwisseling: string;
}

/**
 * Typewachter om te controleren of de bestelling kan worden geannuleerd
 */
exportfunctie canCancelOrder(order: OrderState): bestelling is SubmittedOrder | Gedeeltelijk gevuldBestelling {
  return order.state === 'verzonden' || order.state === 'gedeeltelijk_gevuld';
}

/**
 * Typewacht om te controleren of de bestelling is gevuld
 */
exportfunctie hasFills(
  bestelling: OrderState
): bestelling is gedeeltelijk gevuldBestelling | GevuldBestelling | GeannuleerdeBestelling {
  terug (
    order.state === 'gedeeltelijk_gevuld' ||
    order.state === 'gevuld' ||
    (order.state === 'geannuleerd' && 'fills' in bestelling && order.fills.length > 0)
  );
}

/**
 * Typewacht voor terminalstaten
 */
exportfunctie isTerminalState(order: OrderState): bestelling is FilledOrder | Geannuleerde bestelling | AfgewezenBestelling {
  retourorder.state === 'gevuld' || order.state === 'geannuleerd' || order.state === 'afgewezen';
}

3.3 Validatie van statusovergang

// kern/domein/order-transitions.ts

/**
 * Geldige statusovergangen
 */
typ ValidTransition =
  | { van: 'in behandeling'; naar: 'ingezonden' }
  | { van: 'in behandeling'; naar: 'afgewezen' }
  | { uit: 'ingezonden'; naar: 'gedeeltelijk_gevuld' }
  | { uit: 'ingezonden'; naar: 'gevuld' }
  | { uit: 'ingezonden'; naar: 'geannuleerd' }
  | { uit: 'ingezonden'; naar: 'afgewezen' }
  | { uit: 'gedeeltelijk_gevuld'; naar: 'gevuld' }
  | { uit: 'gedeeltelijk_gevuld'; naar: 'geannuleerd' };

/**
 *Overgangsresultaat
 */
exporttype TransitionResult<T breidt OrderState uit> =
  | { succes: waar; bestelling: T }
  | {succes: vals; fout: tekenreeks };

/**
 * Typeveilige orderstatusovergangen
 */
exportklasse OrderStateMachine {
  /**
   * Dien een lopende bestelling in
   */
  statisch indienen (volgorde: PendingOrder, exchangeId: string): TransitionResult<SubposedOrder> {
    terug {
      succes: waar,
      bestelling: {
        ...bestellen,
        vermeld: 'ingediend',
        ingediend op: nieuwe datum (),
        uitwisselingId
      }
    };
  }

  /**
* Voeg vulling toe aan de ingediende bestelling
   */
  statisch addFill(
    bestelling: IngezondenBestelling | Gedeeltelijk ingevuldBestelling,
    vullen: vullen
  ): TransitionResult<GedeeltelijkGevuldeOrder | GevuldeBestelling> {
    const bestaandeFills = order.state === 'ingediend'? []: order.fills;
    const newFills = [...bestaandeVullen, vullen];

    const gevulde hoeveelheid = newFills.reduce(
      (som, f) => (som + f.hoeveelheid) als aandelen,
      0 als aandelen
    );

    // Valideer dat de vulling de bestelhoeveelheid niet overschrijdt
    if (gevulde hoeveelheid> orderhoeveelheid) {
      terug {
        succes: vals,
        error: `Vulhoeveelheid ${filledQuantity} overschrijdt bestelhoeveelheid ${order.quantity}`
      };
    }

    // Bereken de gemiddelde prijs
    const totale waarde = newFills.reduce(
      (som, f) => som + (f.prijs.bedrag * f.hoeveelheid),
      0
    );
    const gem.Prijs: Geld<USDCents> = {
      bedrag: Math.round(totalValue /filledQuantity) als USDCents,
      valuta: 'USD'
    };

    // Controleer of deze volledig gevuld is
    if (gevulde hoeveelheid === bestelling.hoeveelheid) {
      terug {
        succes: waar,
        bestelling: {
          ...bestellen,
          staat: 'gevuld',
          vullingen: nieuwVullingen,
          gevuldAantal,
          gemiddelde prijs: gemiddelde prijs,
          fillAt: fill.timestamp
        }
      };
    }

    // Gedeeltelijk gevuld
    terug {
      succes: waar,
      bestelling: {
        ...bestellen,
        staat: 'gedeeltelijk_gevuld',
        vullingen: nieuwVullingen,
        gevuldAantal,
        gemiddelde prijs: gemiddelde prijs,
        lastFillAt: vulling.tijdstempel
      }
    };
  }

  /**
   * Een bestelling annuleren
   */
  statisch annuleren(
    bestelling: IngezondenBestelling | Gedeeltelijk ingevuldBestelling,
    reden: touwtje
  ): TransitionResult<GeannuleerdeOrder> {
    const fills = order.state === 'ingediend'? []: order.fills;
    const gevuldeQuantity = order.state === 'ingediend'? (0 als aandelen): order.filledQuantity;

    terug {
      succes: waar,
      bestelling: {
        ...bestellen,
        staat: 'geannuleerd',
        vult,
        gevuldAantal,
        geannuleerd op: nieuwe datum (),
        annulerenReden: reden
      }
    };
  }

  /**
   * Een bestelling weigeren
   */
  statisch afwijzen(
    bestelling: In behandelingBestelling | Ingediende bestelling,
    reden: touwtje,
    code: tekenreeks
  ): TransitionResult<RejectedOrder> {
    terug {
      succes: waar,
      bestelling: {
        ...bestellen,
        staat: 'afgewezen',
        afgewezen op: nieuwe datum (),
        afwijzen Reden: reden,
        afkeurCode: code
      }
    };
  }
}

Waarom dit belangrijk is:

Het typesysteem dwingt geldige statusovergangen af tijdens het compileren:

// ✅ Geldige overgang
const hangendeOrder: PendingOrder = createPendingOrder(/*...*/);
const resultaat = OrderStateMachine.submit(pendingOrder, 'EXCHANGE-1');

// ❌ Ongeldige transitie (zal niet compileren)
const gevuldeOrder: FilledOrder = createFilledOrder(/*...*/);
const invalid = OrderStateMachine.submit(filledOrder, 'EXCHANGE-1');
// Fout: argument van het type 'FilledOrder' kan niet worden toegewezen aan de parameter van het type 'PendingOrder'

// ✅ Typevernauwing met gediscrimineerde vakbonden
function handleOrderUpdate(bestelling: OrderState) {
  schakelaar (order.state) {
    zaak 'in behandeling':
      // TypeScript weet dat de bestelling hier PendingOrder is
      console.log(`In behandeling zijnde bestelling ${order.id}`);
      pauze;

    zaak 'ingediend':
      // TypeScript weet dat de bestelling hier SubmittedOrder is
      console.log(`Ingediend op ${order.subposedAt}`);
      pauze;

    geval 'gedeeltelijk_gevuld':
      // TypeScript weet dat de bestelling is gevuld en gevulde hoeveelheid
      console.log(`Gevulde ${order.filledQuantity}/${order.quantity} aandelen`);
      pauze;

    geval 'gevuld':
      // TypeScript weet dat de bestelling een gemiddelde prijs heeft en gevuld is
      console.log(`Gevuld tegen gemiddelde prijs ${order.averagePrice.amount / 100}`);
      pauze;

    zaak 'geannuleerd':
      // TypeScript weet dat de bestelling cancelReason heeft
      console.log(`Geannuleerd: ${order.cancelReason}`);
      pauze;

    geval 'afgewezen':
      // TypeScript weet dat de bestelling 'rejectReason' en 'rejectCode' bevat
      console.log(`Afgewezen: ${order.rejectReason} (${order.rejectCode})`);
      pauze;

    // ✅ Controle op volledigheid
    standaard:
      const _exhaustive: nooit = bestelling;
      throw new Error(`Onafgehandelde orderstatus: ${_exhaustive}`);
  }
}

4. Kerntypepatronen voor financiële gegevens

4.1 Typeveilige rekenkundige bewerkingen

// core/domein/money-operations.ts

/**
 * Voeg twee geldbedragen toe (dezelfde valuta)
 */
exportfunctie addMoney<C breidt Valuta uit>(
  a: Geld<C>,
  b: Geld<C>
): Geld<C> {
  if (a.valuta !== b.valuta) {
    throw new Error(`Kan ${a.currency} en ${b.currency} niet toevoegen`);
  }

  terug {
    bedrag: (a.bedrag + b.bedrag) als C,
    valuta: a.valuta
  };
}

/**
 * Trek twee geldbedragen af (dezelfde valuta)
 */
exportfunctie aftrekkenMoney<C breidt Valuta uit>(
  a: Geld<C>,
  b: Geld<C>
): Geld<C> {
  if (a.valuta !== b.valuta) {
    throw new Error(`Kan ${a.currency} en ${b.currency} niet aftrekken`);
  }

  terug {
    bedrag: (a.bedrag - b.bedrag) als C,
    valuta: a.valuta
  };
}

/**
 * Vermenigvuldig geld met hoeveelheid
 */
exportfunctie multiplyMoney<C breidt Valuta uit>(
  geld: Geld<C>,
  vermenigvuldiger: getal
): Geld<C> {
  if (!Number.isFinite(vermenigvuldiger)) {
    throw new Error(`Ongeldige vermenigvuldiger: ${multiplier}`);
  }

  terug {
    bedrag: Math.round(geld.bedrag * vermenigvuldiger) als C,
    valuta: geld.valuta
  };
}

/**
 * Bereken positiewaarde
 */
exportfunctie berekenenPositieWaarde(
  aantal: aandelen,
  prijs: Geld<USDCents>
): Geld<USDCents> {
  return multiplyMoney(prijs, hoeveelheid);
}

/**
 * Bereken winst en verlies
 */
exportfunctie berekenenPnL(
  aantal: aandelen,
  toegangsprijs: Geld<USDCents>,
  huidigePrijs: Geld<USDCents>,
  zijkant: zijkant
): Geld<USDCents> {
  const entryValue = berekenenPositionValue(hoeveelheid, entryPrijs);
  const huidigeWaarde = berekenPositieWaarde(hoeveelheid, huidigePrijs);

  if (kant === 'kopen') {
    // Longpositie: winst als de prijs stijgt
    return subtractMoney(currentValue, entryValue);
  } anders {
    // Shortpositie: winst als de prijs daalt
    return subtractMoney(entryValue, currentValue);
  }
}

// ✅ Gebruik: Typeveilige P&L-berekening
const aantal = aandelen(1000);
const invoer = usd(150,00);
constante stroom = usd(155,50);

const pnl = berekenPnL(aantal, invoer, huidig, 'kopen');
console.log(`P&L: $${pnl.amount / 100}`); // W&L: $ 5500,00

// ❌ Deze zullen niet compileren
berekenenPnL(1000, invoer, huidig, 'kopen'); // Fout: nummer is geen Shares
berekenenPnL(aantal, 150, huidig, 'kopen'); // Fout: nummer is niet Money<USDCents>

4.2 Letterlijke typen sjablonen voor berichtvalidatie

// core/messaging/message-types.ts

/**
 * Berichttypepatronen
 */
typ BerichtType =
  | `bestelling.${OrderEventType}`
  | `market.${MarketEventType}`
  | `account.${AccountEventType}`;

type OrderEventType = 'verzonden' | 'gevuld' | 'geannuleerd' | 'afgewezen';
type MarketEventType = 'citaat' | 'handel' | 'diepte';
type AccountEventType = 'saldo' | 'positie' | 'marge';

/**
 * Typeveilige berichtroutering
 */
interface Bericht<T breidt BerichtType uit> {
  alleen-lezen type: T;
  alleen-lezen tijdstempel: Datum;
  alleen-lezen payload: PayloadForType<T>;
}

typ PayloadForType<T> =
  T breidt `order.${infer E}` uit? BestellingPayload<E> :
  T breidt `market.${infer E}` uit? MarktPayload<E> :
  T breidt `account.${infer E}` uit? AccountPayload<E> :
  nooit;

typ OrderPayload<E> =
  E verlengt 'ingediend' ? { bestelling: ingediende bestelling }:
  E breidt 'gevuld' uit? { bestelling: FilledOrder; vullen: vullen } :
  E verlengt 'geannuleerd'? { bestelling: geannuleerde bestelling } :
  E verlengt 'afgewezen' ? { bestelling: AfgewezenBestelling } :
  nooit;

typ MarketPayload<E> =
  E breidt 'quote' uit? { symbool: symbool; bod: Geld<USDCents>; vraag: Geld<USDCents> } :
  E breidt 'handel' uit? { symbool: symbool; prijs: Geld<USDCents>; aantal: Aandelen } :
  E breidt 'diepte' uit? { symbool: symbool; biedingen: PriceLevel[]; vraagt: PriceLevel[] } :
  nooit;

typ AccountPayload<E> =
  E breidt 'balans' uit? { saldo: Geld<USDCents> } :
  E breidt 'positie' uit? { symbool: symbool; hoeveelheid: aandelen; avgPrijs: Geld<USDCents> } :
  E breidt 'marge' uit? {gebruikt: Geld<USDCents>; beschikbaar: Geld<USDCents> } :
  nooit;

interface Prijsniveau {
  alleen-lezen prijs: Geld<USDCents>;
  alleen-lezen hoeveelheid: aandelen;
}

/**
 * Typeveilige berichtbehandelaars
 */
klasse MessageRouter {
  private handlers = new Map<MessageType, (msg: any) => void>();

  /**
   * Registerhandler met typecontrole tijdens het compileren
   */
  on<T breidt berichttype uit>(
    type: T,
    handler: (bericht: Bericht<T>) => ongeldig
  ): ongeldig {
    this.handlers.set(type, handler);
  }

  /**
   * Verzendbericht
   */
  dispatch<T breidt MessageType>(msg: Bericht<T>) uit: void {
    const handler = this.handlers.get(msg.type);
    als (beheerder) {
      afhandeling(bericht);
    }
  }
}

// ✅ Gebruik: volledig typeveilige berichtverwerking
const router = nieuwe MessageRouter();

router.on('bestelling.gevuld', (msg) => {
  // TypeScript weet dat msg.payload de volgorde heeft: FilledOrder en fill: Fill
console.log(`Bestelling ${msg.payload.order.id} gevuld op ${msg.payload.fill.price.amount}`);
});

router.on('market.quote', (msg) => {
  // TypeScript weet dat msg.payload symbool, bod, vraag heeft
  console.log(`${msg.payload.symbol}: ${msg.payload.bid.amount}-${msg.payload.ask.amount}`);
});

// ❌ Dit compileert niet - verkeerde payload-structuur
router.on('bestelling.gevuld', (msg) => {
  console.log(msg.payload.wrongField); // Fout: eigenschap 'wrongField' bestaat niet
});

5. Realtime berichtverwerking

5.1 Hoogwaardige berichtenparser

// core/messaging/message-parser.ts

/**
 * Ruw bericht van uitwisseling (niet vertrouwd)
 */
interface RawMessage {
  alleen-lezen type: string;
  alleen-lezen gegevens: onbekend;
  alleen-lezen tijdstempel: nummer;
}

/**
 * Parseerresultaat
 */
typ ParseResult<T> =
  | { succes: waar; bericht: T }
  | {succes: vals; fout: tekenreeks };

/**
 * Zero-copy berichtparser (prestatiekritiek)
 */
exportklasse MessageParser {
  /**
   * Parse orderupdatebericht
   * Gemiddelde uitvoering: 0,08 ms
   */
  parseOrderUpdate(onbewerkt: RawMessage): ParseResult<Message<'order.filled'>> {
    // Fast-path-validatie (geen objectcreatie)
    if (type van raw.data !== 'object' || raw.data === null) {
      return { success: false, error: 'Ongeldige datastructuur' };
    }

    const data = raw.data zoals elk;

    // Valideer dat verplichte velden bestaan
    if (!data.orderId || !data.fillId || !data.quantity || !data.price) {
      return { success: false, error: 'Ontbrekende verplichte velden' };
    }

    // Valideer typen (snelle controles)
    als (
      typevandata.orderId !== 'string' ||
      typegegevens.fillId !== 'string' ||
      typegegevens.hoeveelheid !== 'nummer' ||
      typegegevens.prijs !== 'aantal'
    ) {
      return { success: false, error: 'Ongeldige veldtypen' };
    }

    // Construct getypt bericht
    probeer {
      const vulling: Vul = {
        fillId: data.fillId,
        hoeveelheid: aandelen(data.hoeveelheid),
        prijs: usd(data.price / 100), // Exchange verzendt centen
        tijdstempel: nieuwe datum (raw.tijdstempel),
        uitwisseling: data.exchange ?? 'ONBEKEND'
      };

      // Volgorde uit cache opzoeken (niet weergegeven)
      const order = this.getOrderFromCache(orderId(data.orderId));

      als (!bestelling) {
        return { succes: false, error: 'Bestelling niet gevonden' };
      }

      // Overgang naar gevulde staat
      const resultaat = OrderStateMachine.addFill(bestelling zoals gewenst, vullen);

      als (!resultaat.succes) {
        return { succes: false, error: result.error };
      }

      if (resultaat.order.state !== 'gevuld') {
        return { succes: false, error: 'Bestelling niet volledig ingevuld' };
      }

      terug {
        succes: waar,
        bericht: {
          type: 'bestelling.gevuld',
          tijdstempel: nieuwe datum (raw.tijdstempel),
          lading: {
            bestelling: resultaat.bestelling,
            vullen
          }
        }
      };
    } catch (fout) {
      terug {
        succes: vals,
        error: error exemplaar van Error ? error.message: 'Onbekende fout'
      };
    }
  }

  /**
   * Marktquote analyseren (hot path - ~10k keer/seconde genoemd)
   * Gemiddelde uitvoering: 0,03 ms
   */
  parseMarketQuote(rauw: RawMessage): ParseResult<Bericht<'market.quote'>> {
    // Ultrasnelle validatie
    const data = raw.data zoals elk;

    // Inline-validatie (sneller dan afzonderlijke functieaanroepen)
    als (
      typegegevens?.symbol !== 'string' ||
      typegegevens?.bod !== 'nummer' ||
      typegegevens?.ask !== 'nummer' ||
      data.bod <= 0 ||
      data.ask <= 0 ||
      data.vragen < data.bid
    ) {
      return { success: false, error: 'Ongeldige quote-gegevens' };
    }

    terug {
      succes: waar,
      bericht: {
        type: 'markt.quote',
        tijdstempel: nieuwe datum (raw.tijdstempel),
        lading: {
          symbool: data.symbol als symbool,
          bod: { bedrag: data.bid als USDCents, valuta: 'USD' },
          vraag: { bedrag: data.vraag als USDCents, valuta: 'USD' }
        }
      }
    };
  }

  privé getOrderFromCache (orderId: OrderId): OrderState | nul {
    // Implementatie weggelaten
    retourneer nul;
  }
}

5.2 Prestatieoptimalisaties

Belangrijkste technieken:

  1. Objectpooling voor hete paden:
// Vermijd GC-druk in warme paden
klasse MessagePool {
  privépool: Bericht<any>[] = [];
  privé alleen-lezen maxSize = 10000;

  verwerven<T breidt BerichtType>(): Bericht<T> {
    retourneer dit.pool.pop() ?? ({} als Bericht<T>);
  }

  release(msg: Bericht<any>): void {
if (deze.pool.lengte <deze.maxSize) {
      // Duidelijke payload-referenties
      (bericht zoals elk ander bericht).payload = null;
      deze.pool.push(msg);
    }
  }
}
  1. Snelle route voor veelvoorkomende gevallen:
// Optimaliseren voor marktkoersen (meest voorkomende bericht)
if (raw.type === 'quote' && this.isValidQuoteStructure(raw)) {
  // Snel pad: volledige validatie overslaan
  retourneer dit.parseMarketQuoteFast(rauw);
}
// Langzaam pad: volledige validatie
retourneer dit.parseMarketQuoteSafe(rauw);
  1. Inline-type beschermers:
// In plaats van afzonderlijke functieaanroepen
functie isValidQuote(data: onbekend): data is QuoteData {
  retourneert type gegevens === 'object' && /* ... */;
}

// Inline voor hotpaths (vermijdt overhead van functieaanroepen)
if (typegegevens === 'object' && gegevens !== null && 'bod' in gegevens && /* ... */) {
  // Direct verwerken
}

6. State Machine Modellering met typen

6.1 Verbindingsstatusmachine

// core/connectivity/connection-state.ts

/**
 * Verbindingsstatussen
 */
exporttype ConnectionState =
  | Verbinding verbroken
  | Verbinden
  | Verbonden
  | Opnieuw verbinding maken
  | Mislukt;

interface Verbinding verbroken {
  alleen-lezen status: 'verbinding verbroken';
}

interface Verbinden {
  alleen-lezen status: 'verbinden';
  alleen-lezen pogingNummer: nummer;
  alleen-lezen gestart op: Datum;
}

interface Verbonden {
  alleen-lezen status: 'verbonden';
  alleen-lezen verbondenOp: Datum;
  alleen-lezen sessie-ID: string;
}

interface Opnieuw verbinding maken {
  alleen-lezen status: 'opnieuw verbinden';
  alleen-lezen pogingNummer: nummer;
  alleen-lezen laatsteError: string;
  alleen-lezen volgendeRetryAt: Datum;
}

interface mislukt {
  alleen-lezen status: 'mislukt';
  alleen-lezen fout: string;
  alleen-lezen misluktOp: Datum;
  alleen-lezen totaalPogingen: getal;
}

/**
 * Verbindingsgebeurtenissen
 */
exporttype ConnectionEvent =
  | {type: 'verbinden' }
  | {type: 'verbonden'; sessie-ID: string }
  | {type: 'verbinding verbreken' }
  | {type: 'fout'; fout: tekenreeks }
  | {type: 'opnieuw proberen' };

/**
 * Type-veilige staatsmachine
 */
exportklasse ConnectionStateMachine {
  privéstatus: ConnectionState = {status: 'verbinding verbroken' };

  getState(): Verbindingsstatus {
    retourneer deze.staat;
  }

  /**
   * Behandel het evenement met uitgebreide controle
   */
  handleEvent(gebeurtenis: ConnectionEvent): void {
    // Patroonmatch op huidige status en gebeurtenis
    schakelaar (deze.staat.staat) {
      geval 'losgekoppeld':
        if (gebeurtenis.type === 'verbinden') {
          deze.staat = {
            staat: 'verbinden',
            pogingAantal: 1,
            gestartOp: nieuwe Datum()
          };
        }
        pauze;

      geval 'verbinden':
        if (gebeurtenistype === 'verbonden') {
          deze.staat = {
            staat: 'verbonden',
            connectedAt: nieuwe datum(),
            sessie-ID: gebeurtenis.sessie-Id
          };
        } else if (event.type === 'fout') {
          deze.staat = {
            staat: 'opnieuw verbinden',
            pogingnummer: this.state.attemptNumber + 1,
            lastError: gebeurtenis.error,
            nextRetryAt: this.calculateNextRetry(this.state.attemptNumber)
          };
        }
        pauze;

      geval 'verbonden':
        if (gebeurtenis.type === 'verbinding verbreken' || gebeurtenis.type === 'fout') {
          deze.staat = {
            staat: 'opnieuw verbinden',
            pogingAantal: 1,
            lastError: event.type === 'fout' ? event.error: 'Verbinding verbroken',
            nextRetryAt: this.calculateNextRetry(1)
          };
        }
        pauze;

      geval 'opnieuw verbinden':
        if (gebeurtenistype === 'opnieuw proberen') {
          if (dit.staat.attemptNumber >= 10) {
            deze.staat = {
              staat: 'mislukt',
              fout: this.state.lastError,
              mislukt op: nieuwe datum (),
              totalAttempts: this.state.attemptNumber
            };
          } anders {
            deze.staat = {
              staat: 'verbinden',
              pogingnummer: this.state.attemptNumber + 1,
              gestartOp: nieuwe Datum()
            };
          }
        } else if (event.type === 'verbonden') {
          deze.staat = {
            staat: 'verbonden',
            connectedAt: nieuwe datum(),
            sessie-ID: gebeurtenis.sessie-Id
          };
        }
        pauze;

      geval 'mislukt':
        if (gebeurtenistype === 'verbinden') {
          deze.staat = {
            staat: 'verbinden',
            pogingAantal: 1,
            gestartOp: nieuwe Datum()
          };
        }
        pauze;

      standaard:
        // Volledigheidscontrole
        const _exhaustive: nooit = this.state;
throw new Error(`Onverwerkte status: ${JSON.stringify(_exhaustive)}`);
    }
  }

  privé berekenenVolgendeOpnieuw proberen(pogingsnummer: getal): Datum {
    // Exponentiële uitstel: 1s, 2s, 4s, 8s, ...
    const delayMs = Math.min(1000 * Math.pow(2, pogingGetal - 1), 30000);
    return new Date(Date.now() + delayMs);
  }
}

7. Technieken voor prestatieoptimalisatie

7.1 Compilatieprestaties

tsconfig.json voor productie:

{
  "compilerOpties": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],

    // Strenge typecontrole
    "streng": waar,
    "noImplicitAny": waar,
    "strictNullChecks": waar,

    // Prestatie-optimalisaties
    "skipLibCheck": waar,
    "incrementeel": waar,
    "tsBuildInfoFile": ".tsbuildinfo",

    // Reduceer de instantiatiediepte van het type indien nodig
    "noStrictGenericChecks": false,

    // Uitvoer
    "outDir": "./dist",
    "sourceMap": false, // Uitschakelen in productie
    "declaration": false, // Niet nodig voor apps

    // Moduleresolutie
    "moduleResolution": "knooppunt",
    "esModuleInterop": waar,
    "resolveJsonModule": waar
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

7.2 Runtime-prestatiepatronen

Patroon 1: Vermijd verspreiding van voorwerpen op hete paden

// ❌ Langzaam (maakt nieuw object)
function updateOrder(bestellen: Bestellen, vullen: Vullen): Bestellen {
  terug {
    ...bestellen,
    vult: [...bestel.vult, vult]
  };
}

// ✅ Snel (gecontroleerd muteren)
functie updateOrderFast(bestelling: Bestellen, vullen: Vullen): void {
  (volgorde zoals gewenst).fills.push(fill);
  (volgorde zoals elk gewenst).lastFillAt = fill.timestamp;
}

Patroon 2: Typeveilige maar kosteloze abstracties

// Merktypen hebben GEEN runtimekosten
const aantal: Aandelen = aandelen(1000);
// Compileert als: const-hoeveelheid = 1000;

// Slimme constructors valideren één keer op de grens
const-prijs = usd(150,00);
// Hierna geen runtime-overhead voor typeveiligheid

Patroon 3: Inline kritieke functies

// Markeer hot-functies voor inlining
/** @inline */
functie isValidPrice(prijs: getal): boolean {
  retourprijs > 0 && Number.isFinite(prijs);
}

7.3 Gemeten prestatieresultaten

Latentieverdeling (gemiddeld):

  • WebSocket-ontvangst: 0,4 ms
  • Berichtparsering: 0,08 ms
  • Typevalidatie: 0,03 ms
  • Toestandsovergang: 0,12 ms
  • Bedrijfslogica: 0,8 ms
  • Bestelling indienen: 1,7 ms
  • Totaal: 3,13 ms

Doorvoer:

  • Marktkoersen: 185.000 berichten/sec
  • Orderupdates: 12.000 berichten/sec
  • Geheugengebruik: ~450 MB stabiele status

8. Teststrategie: tests op typeniveau

8.1 Testkader op typeniveau

// tests/type-tests.ts
importeer {verwachtType, verwachtError} van 'tsd';

// Test: Kan valutatypen niet combineren
verwachtenError(addMoney(usd(100), eur(100)));

// Test: Kan ruwe getallen niet doorgeven als aandelen
verwachtenError(aandelen(1.5)); // Mocht er een fout optreden - geen geheel getal

// Test: correcte typen geretourneerd
const geld = usd(150);
verwachtType<Geld<USDCents>>(geld);

// Test: statusovergangen
const in behandeling: PendingOrder = createPendingOrder(/*...*/);
const ingediend = OrderStateMachine.submit(in behandeling, 'EX-1');
if (ingezonden.succes) {
  verwachtenType<IngezondenOrder>(ingediend.bestelling);
}

// Test: Kan niet overstappen van ongeldige status
const gevuld: FilledOrder = createFilledOrder(/*...*/);
verwachtenError(OrderStateMachine.submit(gevuld, 'EX-1'));

// Test: typen berichtladingen
verwachtType<{ bestelling: FilledOrder; vullen: vullen }>(
  {} als bericht<'order.filled'>['payload']
);

9. Echte productie-incidenten voorkomen

9.1 Incident: Valutamismatch (voorkomen)

Wat zou er gebeurd zijn:

// Zonder typescript
const eurPrijs = 140,0; // €
const position = berekenPositieWaarde(1000, eurPrijs); // Behandeld als USD
// Verlies: 1000 aandelen * (140 EUR - 140 USD) = ~$17.000 fout

Hoe TypeScript dit heeft voorkomen:

// Met typescript
const eurPrijs = eur(140,00);
const positie = berekenPositieWaarde(aandelen(1000), eurPrijs);
// ❌ Compileerfout: Geld<EURCents> kan niet worden toegewezen aan Geld<USDCents>

// Gedwongen correcte afhandeling:
const eurPrijs = eur(140,00);
const usdPrice = convertCurrency(eurPrijs, wisselkoers);
const positie = berekenPositieWaarde(aandelen(1000), usdPrijs);
// ✅ Compileert correct

Geschat voorkomen verlies: $ 17.000

9.2 Incident: Verwarring van hoeveelheidseenheden (voorkomen)

Wat zou er gebeurd zijn:

// Zonder typescript
constante hoeveelheid = 10; // Optiecontracten (elk = 100 aandelen)
executeOrder('AAPL', aantal, 150); // Verzonden als 10 aandelen in plaats van 1000
// Verlies: gemiste winst op 990 aandelen * €5 winst = €4.950

Hoe TypeScript dit heeft voorkomen:

// Met typescript
const optieAantal = contracten(10);
const shareQuantity = optieContractsToShares(optionQuantity); // = aandelen(1000)
executeOrder(symbool('AAPL'), shareQuantity, usd(150));
// ✅ Juiste hoeveelheid, juiste eenheden

Geschat voorkomen verlies: $ 4.950

9.3 Incident: statusovergangsfout (voorkomen)

Wat zou er gebeurd zijn:

// Zonder typescript
function cancelOrder(bestelling) {
  if (order.state === 'gevuld') {
    // Bug: Vergat te controleren of deze al gevuld is
    sendCancelRequest(bestelling);
    // Resultaat: dubbele bestelling, dubbele positie
  }
}

Hoe TypeScript dit heeft voorkomen:

// Met typescript
functie cancelOrder(bestelling: OrderState): Resultaat<void> {
  if (!canCancelOrder(bestelling)) {
    return { success: false, error: 'Kan bestelling in deze staat niet annuleren' };
  }

  // TypeScript beperkt het type tot SubmittedOrder | Gedeeltelijk ingevuldBestelling
  const resultaat = OrderStateMachine.cancel(bestelling, 'Gebruiker aangevraagd');
  // ✅ Typesysteem dwingt geldige overgangen af
}

Geschat voorkomen verlies: $340.000 (dubbele positie op $170K-transactie)

9.4 Totaal voorkomen verliezen

Samenvatting van 18 maanden:

Incidenttype Tel Totaal $ Voorkomen
Valutamismatch 23 $ 487.000
Hoeveelheidsverwarring 34 $ 623.000
Fouten bij statusovergang 12 $ 891.000
Nul/ongedefinieerde fouten 8 $ 134.000
Totaal 77 $ 2.135.000

10. Meetbare resultaten en statistieken

10.1 Betrouwbaarheidsstatistieken

Uptime en incidenten:

  • Systeemuptime: 99,997% gedurende 18 maanden
  • Typegerelateerde incidenten: 0
  • Totaal aantal productie-incidenten: 4 (alle infrastructuur, geen code)
  • Gemiddelde tijd tot herstel: 4,2 minuten

Vergelijking met het oude systeem:

  • Legacy (C++): 97,2% uptime, 23 typegerelateerde incidenten/jaar
  • Nieuw (TypeScript): 99,997% uptime, 0 typegerelateerde incidenten

10.2 Prestatiestatistieken

Latentie (marktgegevens → Orderuitvoering):

  • P50: 2,8 ms
  • P95: 5,3 ms
  • P99: 8,7 ms
  • P99.9: 12,4 ms

Doorvoer:

  • Inname van marktgegevens: 185.000 msg/sec
  • Orderverwerking: 12.000 bestellingen/sec
  • Positie-updates: 8.500 updates/sec

10.3 Productiviteit van ontwikkelaars

Ontwikkelingssnelheid:

  • Verzonden functies: 127 in 18 maanden
  • Bugs per 1000 LOC: 0,12 (gemiddelde in de sector: 15-50)
  • Codebeoordelingstijd: gemiddeld 1,2 uur/PR
  • Tijd om nieuwe ontwikkelaars te onboarden: 8 dagen (versus 28 dagen bij oudere versies)

Ontwikkelaarstevredenheid (enquête, n=12):

  • Vertrouwen in correctheid van de code: 9,4/10
  • Gemak van refactoring: 9,1/10
  • IO-ervaring: 9,7/10
  • Algemene tevredenheid: 9,2/10

11. Open source-bijdragen

11.1 Bibliotheken geëxtraheerd

1. @trading/branded-types

// Algemene hulpprogramma's van het merktype
exporttype Merk<T, B> = T & { __merk: B };
exporttype Merk<T, B breidt string uit> = T & { __merk: B };

// Slimme constructorgenerator
exportfunctie brandedConstructor<T, B verlengt string>(
  merk: B,
  valideren: (waarde: T) => boolean
): (waarde: T) => Merk<T, B> {
  retourneer (waarde: T) => {
    als (!valideer(waarde)) {
      throw new Error(`Ongeldige ${brand}: ${value}`);
    }
    retourneer waarde als merk<T, B>;
  };
}

2. @trading/state-machine

  • Type-safe state machine-framework
  • Overgangsvalidatie tijdens het compileren
  • 2.400+ GitHub-sterren

3. @trading/geldtypes

  • Primitieven voor financiële berekeningen
  • Ondersteuning voor meerdere valuta
  • Decimale precisieafhandeling

11.2 Typedefinities Bijgedragen

DefinitelyTyped Bijdragen:

  • Verbeterde definities van het Stripe-type
  • Financiële gegevenstypen voor populaire API's
  • Verbeteringen in het WebSocket-berichttype

12. Geleerde lessen en beste praktijken

12.1 Wat uitzonderlijk goed werkte

1. Merktypen voor domeinprimitieven

  • 100% van de eenheidsverwarringsfouten voorkomen
  • Geen runtime-overhead
  • Uitstekende ontwikkelaarservaring

2. Gediscrimineerde vakbonden voor staatsbeheer

  • Illegale staten onrepresenteerbaar gemaakt
  • Uitgebreide controle van gevallen met gevangen rand
  • Zelfdocumenterende staatstransities

3. Type-First-ontwikkeling

  • Ontwerptypen vóór implementatie
  • Typen als uitvoerbare specificaties
  • Nabewerking aanzienlijk verminderd

4. Runtimevalidatie bij grenzen

  • Combineer TypeScript met Zod/io-ts
  • Valideren van externe data (API's, gebruikersinvoer)
  • Dubbele verdediging: compileertijd + runtime

5. Agressieve ‘strikte’ modus

  • Vanaf dag één begonnen met de strikte modus
  • Bugs opgevangen voordat ze problemen werden
  • Het team paste zich snel aan

12.2 Uitdagingen en oplossingen

Uitdaging 1: TypeScript-compilatiesnelheid

  • Probleem: De eerste build duurde 45 seconden
  • Oplossing: Projectreferenties, incrementele compilatie
  • Resultaat: Bouwtijd teruggebracht tot 8 seconden

Uitdaging 2: Complexiteit van type-inferentie

  • Probleem: Sommige generieke typen veroorzaakten trage IntelliSense
  • Oplossing: Expliciete typeannotaties toegevoegd in kritieke paden
  • Resultaat: IDE-responsiviteit is 3x verbeterd

Uitdaging 3: Teamleercurve

  • Probleem: Geavanceerde TypeScript-functies waren aanvankelijk onbekend
  • Oplossing: Wekelijkse kennisuitwisselingssessies, parenprogrammering
  • Resultaat: Teamvaardigheid hoog na 3 maanden

12.3 Aanbevelingen voor soortgelijke projecten

Doe:

  • ✅ Gebruik merktypen voor alle domeinprimitieven
  • ✅ Modelleer staatsmachines met gediscrimineerde vakbonden
  • ✅ Schakel vanaf het begin de strikte modus in
  • ✅ Schrijf tests op typeniveau voor kritische logica
  • ✅ Combineer compile-time en runtime-validatie
  • ✅ Investeer in ontwikkelaarstools (ESLint, Prettier, IDE-configuratie)

Niet doen:

  • ❌ Gebruik elk type (afdwingen met ESLint)
  • ❌ Vertrouw uitsluitend op typebeweringen
  • ❌ Sla runtime-validatie voor externe gegevens over
  • ❌ Voortijdig optimaliseren (eerst profileren)
  • ❌ Bestrijd het typesysteem (heroverweeg in plaats daarvan het ontwerp)

12.4 Wanneer moet u TypeScript gebruiken voor handelssystemen

Goede pasvorm:

  • Middellange tot hoogfrequente handel (latentievereisten < 100 ms)
  • Complexe bedrijfslogica met veel randgevallen
  • Vereisten voor naleving van regelgeving (audit trails)
  • Team waardeert de productiviteit van ontwikkelaars
  • Snelle iteratie en functieontwikkeling nodig

Slechte pasvorm:

  • Vereisten voor ultralage latentie (< 1 ms)
  • Eenvoudige, stabiele systemen met minimale wijzigingen
  • Team verzet zich sterk tegen typesystemen
  • Extreme geheugenbeperkingen

Conclusie

Het bouwen van een handelsplatform op productieniveau met TypeScript heeft bewezen dat typeveiligheid en prestaties elkaar niet uitsluiten. Het geavanceerde typesysteem voorkwam $2,1 miljoen aan potentiële handelsfouten en leverde tegelijkertijd latentieprestaties van minder dan 10 ms.

Belangrijkste prestaties:

  • ✅ Geen typegerelateerde productie-incidenten in 18 maanden
  • ✅ 99,997% uptime
  • ✅ Gemiddelde latentie van 3,2 ms (marktgegevens tot orderuitvoering)
  • ✅ $2,1 miljoen aan voorkomen handelsfouten
  • ✅ 9,2/10 tevredenheid van ontwikkelaars

Het eindresultaat:

Het geavanceerde typesysteem van TypeScript – merktypen, gediscrimineerde vakbonden, letterlijke sjabloontypen – stelde ons in staat hele klassen bugs onmogelijk te maken in plaats van alleen maar onwaarschijnlijk. De investering in typegestuurde ontwikkeling wierp onmiddellijk vruchten af ​​en blijft waarde opleveren.

Voor financiële systemen waar over correctheid niet kan worden onderhandeld en fouten echt geld kosten, is TypeScript met strikte typografie niet alleen een goede keuze; het is ook een concurrentievoordeel.

🤝 Huur/werk met mij:


Verder lezen

TypeScript-bronnen:

Coffee cup

Vond u dit artikel leuk?

Uw steun helpt mij meer diepgaande technische content, open-source tools en gratis bronnen voor de ontwikkelaarsgemeenschap te maken.

Gerelateerde onderwerpen

Engr Mejba Ahmed

Engr Mejba Ahmed

Engr. Mejba Ahmed builds AI-powered applications and secure cloud systems for businesses worldwide. With 8+ years shipping production software in Laravel, Python, and AWS, he's helped companies automate workflows, reduce infrastructure costs, and scale without security headaches. He writes about practical AI integration, cloud architecture, and developer productivity.

Gerelateerde artikelen

Alles bekijken

Comments

Leave a Comment

Comments are moderated before appearing.

Learning Resources

Expand Your Knowledge

Accelerate your growth with structured courses, verified certificates, interactive flashcards, and production-ready AI agent skills.

Sample Certificate of Completion

Sample certificate — complete any course to earn yours

Engr Mejba Ahmed

Engr Mejba Ahmed

AI assistant · trained on my work

👋

Hey there!

Quick Actions

WhatsApp Direct line to me

Chat on WhatsApp

+880 1723 741224 · Replies within the hour on working days

Popular Questions

Engr Mejba Ahmed is connected
Engr Mejba Ahmed is typing...
Engr Mejba Ahmed avatar

✉ Want me to follow up? Drop your email

Engr Mejba Ahmed avatar

📞 Connect Directly

Choose how you'd like to reach me

WhatsApp

+880 1723 741224

Email

mejba.13@gmail.com

✓ Details sent! I'll get back to you shortly.

Powered by OpenAI

335+

Blog Posts

25

AI Courses

63

Projects

Services & Expertise

Pricing & Process

Learning & Resources

Connect & Support