← guides

TypeScript for Data Science

Production data pipelines without leaving your codebase · Updated May 2026

TL;DR

You're building data pipelines or analytics tools in TypeScript and wondering if you need to rewrite everything in Python. Short answer: probably not. TypeScript has a viable data science stack in 2026—Arquero for DataFrames (210k monthly downloads), DuckDB for SQL analytics, Observable Plot for visualization. Python still wins for exploratory analysis and ML training, but TypeScript excels at production deployment, type-safe pipelines, and integrated backend data handling.

Why TypeScript for Data Science?

The Problem

You're a TypeScript developer working on a web application. Product asks for:

Your instinct: spin up a Python microservice. But now you're maintaining two codebases, two deployment pipelines, and a serialization boundary between them. Every schema change requires updating both sides. Your TypeScript team can't debug the Python service. Your iteration speed drops.

The alternative: Handle the data science work in TypeScript, using the same runtime, types, and deployment pipeline as your application layer.

When TypeScript Makes Sense

Use TypeScript when... Stick with Python when...
Building production applications with embedded analytics Doing exploratory data analysis in Jupyter notebooks
Need type-safe data transformations in existing codebase Need statistical modeling (scikit-learn, statsmodels)
Want fast iteration without a separate Python service Training ML models (PyTorch, TensorFlow)
Data fits in memory (up to several GB) Working with scientific computing libraries (NumPy, SciPy)
Team is TypeScript-native and deployment simplicity matters Team is primarily data scientists, not web developers

The Honest Comparison

From the Leanware TypeScript vs Python article:

"For data science work, pandas, numpy, matplotlib, and Jupyter notebooks form Python's data science stack, with nothing in the TypeScript world coming close for exploratory data analysis."

True. But the same article notes:

"TypeScript requires developers to invest time in type definitions, but this upfront investment pays dividends in developer productivity and code maintenance."

The 2026 reality: Use both. Python for research and model training, TypeScript for production deployment and application integration.

Quick Win: Your First TypeScript Data Pipeline

Let's build a real data pipeline in TypeScript. Problem: analyze product sales from a CSV, calculate category totals, and find top performers.

Install dependencies (using Bun)

bun add arquero @observablehq/plot

The code (sales-analysis.ts)

import * as aq from 'arquero'; import * as Plot from '@observablehq/plot'; // Load CSV data const sales = await aq.loadCSV('sales.csv'); // Calculate category totals const categoryTotals = sales .groupby('category') .rollup({ total_revenue: aq.escape(d => aq.op.sum(d.revenue)), avg_price: aq.escape(d => aq.op.mean(d.price)), count: aq.escape(d => aq.op.count()) }) .orderby(aq.desc('total_revenue')); console.log(categoryTotals.print()); // Find products above 90th percentile const p90 = sales.rollup({ p90: aq.escape(d => aq.op.quantile(d.revenue, 0.9)) }).get('p90', 0); const topProducts = sales .filter(aq.escape(d => d.revenue > p90)) .select('product', 'category', 'revenue') .orderby(aq.desc('revenue')); console.log(`Products above 90th percentile (${p90}):`); console.log(topProducts.print());

Run it

bun run sales-analysis.ts

What this demonstrates:

The Stack: Libraries That Actually Work

DataFrames: Arquero

210k monthly downloads 1,517 GitHub stars Last commit: 2025-05-29

Why Arquero over alternatives (Danfo.js, data-forge-ts, pandas-js):

Core operations:

import * as aq from 'arquero'; import { op } from 'arquero'; const data = aq.table({ date: ['2026-01-01', '2026-01-02', '2026-01-03'], value: [100, 150, 120], category: ['A', 'B', 'A'] }); // Transformations const result = data .derive({ normalized: d => d.value / op.max(d.value), date_parsed: d => op.parse_date(d.date) }) .filter(d => d.category === 'A') .select('date', 'value', 'normalized'); // Aggregations const summary = data .groupby('category') .rollup({ mean: d => op.mean(d.value), median: d => op.median(d.value), std: d => op.stdev(d.value) }); // Export const csv = result.toCSV(); const json = result.objects(); const arrow = result.toArrow();

Visualization: Observable Plot

5,277 GitHub stars Last commit: 2026-05-16

Why Observable Plot:

Runtime Choice: Bun vs. Deno vs. Node.js

TypeScript execution models differ across runtimes:

Performance comparison (2026 benchmarks):

Metric Bun Deno Node.js
HTTP throughput 52k req/sec 29k req/sec 14k req/sec
Cold start 8-15ms 40-60ms 60-120ms

Recommendation:

Patterns: Real-World TypeScript Data Engineering

Pattern 1: Type-Safe ETL Pipelines

Problem: You're ingesting data from an API, transforming it, and loading it into a database. You want compile-time guarantees that your transformations are correct.

Solution: Use Zod for schema validation and TypeScript types for transformations.

import * as aq from 'arquero'; import { op } from 'arquero'; import { z } from 'zod'; const SalesRecordSchema = z.object({ id: z.string(), product_id: z.string(), amount: z.number().positive(), timestamp: z.string().datetime(), customer_id: z.string().optional() }); type SalesRecord = z.infer; async function extractTransformLoad(sourceUrl: string): Promise { const response = await fetch(sourceUrl); const rawData = await response.json(); // Validate schema const validated = rawData.map((row: unknown) => SalesRecordSchema.parse(row) ); // Transform with Arquero const table = aq.from(validated) .derive({ amount_usd: d => d.amount / 100, date: d => op.parse_date(d.timestamp), hour: d => op.hour(op.parse_date(d.timestamp)) }) .filter(d => d.amount_usd > 0) .select('id', 'product_id', 'amount_usd', 'date', 'hour', 'customer_id'); return table; }

Benefits:

Pattern 2: Streaming Aggregations

Problem: You're building a real-time analytics dashboard. Data arrives in batches; you need to update aggregates efficiently.

Solution: Maintain aggregated state with Arquero, update incrementally.

See the full guide for complete code examples.

When to Switch from Python

You Should Switch When...

The Hybrid Approach (Recommended)

From the Medium AI article:

"The smartest strategy for 2026 is using both—TypeScript for the application layer, and Python for the AI/ML layer."

Architecture:

Example workflow:

  1. Data scientist trains churn prediction model in Python (PyTorch + pandas)
  2. Model exported to ONNX format
  3. TypeScript application loads ONNX model via onnxruntime-web
  4. Real-time inference runs in TypeScript (browser or server)
  5. Feature engineering pipeline rebuilt in TypeScript using Arquero (guarantees consistency)

Troubleshooting Common Issues

"Arquero rollup() returns NaN for aggregations"

Symptom: op.mean(d.value) returns NaN even though your data looks correct.

Cause: Column values are strings, not numbers. CSV parsing infers types, but sometimes incorrectly.

Fix: Explicitly parse columns with op.parse_float(d.value)

"Column types inferred incorrectly from CSV"

Symptom: Numeric columns treated as strings, aggregations fail.

Cause: CSV has no schema - everything is text until parsed.

Fix: Use Zod to define schema and enforce types at load time.

"Out of memory error with large datasets"

Symptom: Node.js crashes with OOM when processing large files.

Cause: Loading entire dataset into memory as JavaScript objects.

Fix: Process in chunks or use DuckDB via Node.js native bindings (not WASM).

Memory ceiling: Arquero and browser-based tools work best with datasets under 2GB. For larger data, use server-side DuckDB or a proper database.

Resources

Official Documentation

GitHub Repositories

Example Projects

Verified Data (as of 2026-05-25)